mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-12 13:39:04 +08:00
feat: show more usage info in billing page (#4808)
This commit is contained in:
parent
11f173693b
commit
a7fb1ffcd8
@ -0,0 +1,6 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="file-upload">
|
||||||
|
<path id="Icon" d="M20 10.5V6.8C20 5.11984 20 4.27976 19.673 3.63803C19.3854 3.07354 18.9265 2.6146 18.362 2.32698C17.7202 2 16.8802 2 15.2 2H8.8C7.11984 2 6.27976 2 5.63803 2.32698C5.07354 2.6146 4.6146 3.07354 4.32698 3.63803C4 4.27976 4 5.11984 4 6.8V17.2C4 18.8802 4 19.7202 4.32698 20.362C4.6146 20.9265 5.07354 21.3854 5.63803 21.673C6.27976 22 7.11984 22 8.8 22H12M14 11H8M10 15H8M16 7H8" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path id="Icon_2" d="M15 18L18 15M18 15L21 18M18 15L18 21" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 746 B |
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"type": "element",
|
||||||
|
"isRootNode": true,
|
||||||
|
"name": "svg",
|
||||||
|
"attributes": {
|
||||||
|
"width": "24",
|
||||||
|
"height": "24",
|
||||||
|
"viewBox": "0 0 24 24",
|
||||||
|
"fill": "none",
|
||||||
|
"xmlns": "http://www.w3.org/2000/svg"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"attributes": {
|
||||||
|
"id": "file-upload"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"id": "Icon",
|
||||||
|
"d": "M20 10.5V6.8C20 5.11984 20 4.27976 19.673 3.63803C19.3854 3.07354 18.9265 2.6146 18.362 2.32698C17.7202 2 16.8802 2 15.2 2H8.8C7.11984 2 6.27976 2 5.63803 2.32698C5.07354 2.6146 4.6146 3.07354 4.32698 3.63803C4 4.27976 4 5.11984 4 6.8V17.2C4 18.8802 4 19.7202 4.32698 20.362C4.6146 20.9265 5.07354 21.3854 5.63803 21.673C6.27976 22 7.11984 22 8.8 22H12M14 11H8M10 15H8M16 7H8",
|
||||||
|
"stroke": "currentColor",
|
||||||
|
"stroke-width": "2",
|
||||||
|
"stroke-linecap": "round",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"id": "Icon_2",
|
||||||
|
"d": "M15 18L18 15M18 15L21 18M18 15L18 21",
|
||||||
|
"stroke": "currentColor",
|
||||||
|
"stroke-width": "2",
|
||||||
|
"stroke-linecap": "round",
|
||||||
|
"stroke-linejoin": "round"
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "FileUpload"
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
// GENERATE BY script
|
||||||
|
// DON NOT EDIT IT MANUALLY
|
||||||
|
|
||||||
|
import * as React from 'react'
|
||||||
|
import data from './FileUpload.json'
|
||||||
|
import IconBase from '@/app/components/base/icons/IconBase'
|
||||||
|
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||||
|
|
||||||
|
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||||
|
props,
|
||||||
|
ref,
|
||||||
|
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||||
|
|
||||||
|
Icon.displayName = 'FileUpload'
|
||||||
|
|
||||||
|
export default Icon
|
@ -7,4 +7,5 @@ export { default as FileDownload02 } from './FileDownload02'
|
|||||||
export { default as FilePlus01 } from './FilePlus01'
|
export { default as FilePlus01 } from './FilePlus01'
|
||||||
export { default as FilePlus02 } from './FilePlus02'
|
export { default as FilePlus02 } from './FilePlus02'
|
||||||
export { default as FileText } from './FileText'
|
export { default as FileText } from './FileText'
|
||||||
|
export { default as FileUpload } from './FileUpload'
|
||||||
export { default as Folder } from './Folder'
|
export { default as Folder } from './Folder'
|
||||||
|
@ -86,11 +86,13 @@ export const defaultPlan = {
|
|||||||
buildApps: 1,
|
buildApps: 1,
|
||||||
teamMembers: 1,
|
teamMembers: 1,
|
||||||
annotatedResponse: 1,
|
annotatedResponse: 1,
|
||||||
|
documentsUploadQuota: 1,
|
||||||
},
|
},
|
||||||
total: {
|
total: {
|
||||||
vectorSpace: 10,
|
vectorSpace: 10,
|
||||||
buildApps: 10,
|
buildApps: 10,
|
||||||
teamMembers: 1,
|
teamMembers: 1,
|
||||||
annotatedResponse: 10,
|
annotatedResponse: 10,
|
||||||
|
documentsUploadQuota: 50,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,11 @@ import { Plan } from '../type'
|
|||||||
import VectorSpaceInfo from '../usage-info/vector-space-info'
|
import VectorSpaceInfo from '../usage-info/vector-space-info'
|
||||||
import AppsInfo from '../usage-info/apps-info'
|
import AppsInfo from '../usage-info/apps-info'
|
||||||
import UpgradeBtn from '../upgrade-btn'
|
import UpgradeBtn from '../upgrade-btn'
|
||||||
|
import { User01 } from '../../base/icons/src/vender/line/users'
|
||||||
|
import { MessageFastPlus } from '../../base/icons/src/vender/line/communication'
|
||||||
|
import { FileUpload } from '../../base/icons/src/vender/line/files'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
|
import UsageInfo from '@/app/components/billing/usage-info'
|
||||||
|
|
||||||
const typeStyle = {
|
const typeStyle = {
|
||||||
[Plan.sandbox]: {
|
[Plan.sandbox]: {
|
||||||
@ -41,6 +45,11 @@ const PlanComp: FC<Props> = ({
|
|||||||
type,
|
type,
|
||||||
} = plan
|
} = plan
|
||||||
|
|
||||||
|
const {
|
||||||
|
usage,
|
||||||
|
total,
|
||||||
|
} = plan
|
||||||
|
|
||||||
const isInHeader = loc === 'header'
|
const isInHeader = loc === 'header'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -76,8 +85,30 @@ const PlanComp: FC<Props> = ({
|
|||||||
|
|
||||||
{/* Plan detail */}
|
{/* Plan detail */}
|
||||||
<div className='rounded-xl bg-white px-6 py-3'>
|
<div className='rounded-xl bg-white px-6 py-3'>
|
||||||
<VectorSpaceInfo className='py-3' />
|
|
||||||
|
<UsageInfo
|
||||||
|
className='py-3'
|
||||||
|
Icon={User01}
|
||||||
|
name={t('billing.plansCommon.teamMembers')}
|
||||||
|
usage={usage.teamMembers}
|
||||||
|
total={total.teamMembers}
|
||||||
|
/>
|
||||||
<AppsInfo className='py-3' />
|
<AppsInfo className='py-3' />
|
||||||
|
<VectorSpaceInfo className='py-3' />
|
||||||
|
<UsageInfo
|
||||||
|
className='py-3'
|
||||||
|
Icon={MessageFastPlus}
|
||||||
|
name={t('billing.plansCommon.annotationQuota')}
|
||||||
|
usage={usage.annotatedResponse}
|
||||||
|
total={total.annotatedResponse}
|
||||||
|
/>
|
||||||
|
<UsageInfo
|
||||||
|
className='py-3'
|
||||||
|
Icon={FileUpload}
|
||||||
|
name={t('billing.plansCommon.documentsUploadQuota')}
|
||||||
|
usage={usage.documentsUploadQuota}
|
||||||
|
total={total.documentsUploadQuota}
|
||||||
|
/>
|
||||||
{isInHeader && type === Plan.sandbox && (
|
{isInHeader && type === Plan.sandbox && (
|
||||||
<UpgradeBtn
|
<UpgradeBtn
|
||||||
className='flex-shrink-0 my-3'
|
className='flex-shrink-0 my-3'
|
||||||
|
@ -28,7 +28,7 @@ export type PlanInfo = {
|
|||||||
annotatedResponse: number
|
annotatedResponse: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UsagePlanInfo = Pick<PlanInfo, 'vectorSpace' | 'buildApps' | 'teamMembers' | 'annotatedResponse'>
|
export type UsagePlanInfo = Pick<PlanInfo, 'vectorSpace' | 'buildApps' | 'teamMembers' | 'annotatedResponse' | 'documentsUploadQuota'>
|
||||||
|
|
||||||
export enum DocumentProcessingPriority {
|
export enum DocumentProcessingPriority {
|
||||||
standard = 'standard',
|
standard = 'standard',
|
||||||
@ -59,6 +59,10 @@ export type CurrentPlanInfoBackend = {
|
|||||||
size: number
|
size: number
|
||||||
limit: number // total. 0 means unlimited
|
limit: number // total. 0 means unlimited
|
||||||
}
|
}
|
||||||
|
documents_upload_quota: {
|
||||||
|
size: number
|
||||||
|
limit: number // total. 0 means unlimited
|
||||||
|
}
|
||||||
docs_processing: DocumentProcessingPriority
|
docs_processing: DocumentProcessingPriority
|
||||||
can_replace_logo: boolean
|
can_replace_logo: boolean
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,14 @@ export const parseCurrentPlan = (data: CurrentPlanInfoBackend) => {
|
|||||||
buildApps: data.apps?.size || 0,
|
buildApps: data.apps?.size || 0,
|
||||||
teamMembers: data.members.size,
|
teamMembers: data.members.size,
|
||||||
annotatedResponse: data.annotation_quota_limit.size,
|
annotatedResponse: data.annotation_quota_limit.size,
|
||||||
|
documentsUploadQuota: data.documents_upload_quota.size,
|
||||||
},
|
},
|
||||||
total: {
|
total: {
|
||||||
vectorSpace: parseLimit(data.vector_space.limit),
|
vectorSpace: parseLimit(data.vector_space.limit),
|
||||||
buildApps: parseLimit(data.apps?.limit) || 0,
|
buildApps: parseLimit(data.apps?.limit) || 0,
|
||||||
teamMembers: parseLimit(data.members.limit),
|
teamMembers: parseLimit(data.members.limit),
|
||||||
annotatedResponse: parseLimit(data.annotation_quota_limit.limit),
|
annotatedResponse: parseLimit(data.annotation_quota_limit.limit),
|
||||||
|
documentsUploadQuota: parseLimit(data.documents_upload_quota.limit),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect } from 'react'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useBoolean, useClickAway } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import { useSelectedLayoutSegment } from 'next/navigation'
|
import { useSelectedLayoutSegment } from 'next/navigation'
|
||||||
import { Bars3Icon } from '@heroicons/react/20/solid'
|
import { Bars3Icon } from '@heroicons/react/20/solid'
|
||||||
import HeaderBillingBtn from '../billing/header-billing-btn'
|
import HeaderBillingBtn from '../billing/header-billing-btn'
|
||||||
@ -15,9 +15,9 @@ import GithubStar from './github-star'
|
|||||||
import { WorkspaceProvider } from '@/context/workspace-context'
|
import { WorkspaceProvider } from '@/context/workspace-context'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import LogoSite from '@/app/components/base/logo/logo-site'
|
import LogoSite from '@/app/components/base/logo/logo-site'
|
||||||
import PlanComp from '@/app/components/billing/plan'
|
|
||||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||||
import { useProviderContext } from '@/context/provider-context'
|
import { useProviderContext } from '@/context/provider-context'
|
||||||
|
import { useModalContext } from '@/context/modal-context'
|
||||||
|
|
||||||
const navClassName = `
|
const navClassName = `
|
||||||
flex items-center relative mr-0 sm:mr-3 px-3 h-8 rounded-xl
|
flex items-center relative mr-0 sm:mr-3 px-3 h-8 rounded-xl
|
||||||
@ -26,18 +26,21 @@ const navClassName = `
|
|||||||
`
|
`
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
const { isCurrentWorkspaceManager, langeniusVersionInfo } = useAppContext()
|
const { isCurrentWorkspaceManager } = useAppContext()
|
||||||
const [showUpgradePanel, setShowUpgradePanel] = useState(false)
|
|
||||||
const upgradeBtnRef = useRef<HTMLElement>(null)
|
|
||||||
useClickAway(() => {
|
|
||||||
setShowUpgradePanel(false)
|
|
||||||
}, upgradeBtnRef)
|
|
||||||
|
|
||||||
const selectedSegment = useSelectedLayoutSegment()
|
const selectedSegment = useSelectedLayoutSegment()
|
||||||
const media = useBreakpoints()
|
const media = useBreakpoints()
|
||||||
const isMobile = media === MediaType.mobile
|
const isMobile = media === MediaType.mobile
|
||||||
const [isShowNavMenu, { toggle, setFalse: hideNavMenu }] = useBoolean(false)
|
const [isShowNavMenu, { toggle, setFalse: hideNavMenu }] = useBoolean(false)
|
||||||
const { enableBilling } = useProviderContext()
|
const { enableBilling, plan } = useProviderContext()
|
||||||
|
const { setShowPricingModal, setShowAccountSettingModal } = useModalContext()
|
||||||
|
const isFreePlan = plan.type === 'sandbox'
|
||||||
|
const handlePlanClick = useCallback(() => {
|
||||||
|
if (isFreePlan)
|
||||||
|
setShowPricingModal()
|
||||||
|
else
|
||||||
|
setShowAccountSettingModal({ payload: 'billing' })
|
||||||
|
}, [isFreePlan, setShowAccountSettingModal, setShowPricingModal])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
hideNavMenu()
|
hideNavMenu()
|
||||||
@ -79,15 +82,7 @@ const Header = () => {
|
|||||||
<EnvNav />
|
<EnvNav />
|
||||||
{enableBilling && (
|
{enableBilling && (
|
||||||
<div className='mr-3 select-none'>
|
<div className='mr-3 select-none'>
|
||||||
<HeaderBillingBtn onClick={() => setShowUpgradePanel(true)} />
|
<HeaderBillingBtn onClick={handlePlanClick} />
|
||||||
{showUpgradePanel && (
|
|
||||||
<div
|
|
||||||
ref={upgradeBtnRef as any}
|
|
||||||
className='fixed z-10 top-12 right-1 w-[360px]'
|
|
||||||
>
|
|
||||||
<PlanComp loc='header' />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<WorkspaceProvider>
|
<WorkspaceProvider>
|
||||||
|
@ -40,7 +40,7 @@ const ModalContext = createContext<{
|
|||||||
setShowApiBasedExtensionModal: Dispatch<SetStateAction<ModalState<ApiBasedExtension> | null>>
|
setShowApiBasedExtensionModal: Dispatch<SetStateAction<ModalState<ApiBasedExtension> | null>>
|
||||||
setShowModerationSettingModal: Dispatch<SetStateAction<ModalState<ModerationConfig> | null>>
|
setShowModerationSettingModal: Dispatch<SetStateAction<ModalState<ModerationConfig> | null>>
|
||||||
setShowExternalDataToolModal: Dispatch<SetStateAction<ModalState<ExternalDataTool> | null>>
|
setShowExternalDataToolModal: Dispatch<SetStateAction<ModalState<ExternalDataTool> | null>>
|
||||||
setShowPricingModal: Dispatch<SetStateAction<any>>
|
setShowPricingModal: () => void
|
||||||
setShowAnnotationFullModal: () => void
|
setShowAnnotationFullModal: () => void
|
||||||
setShowModelModal: Dispatch<SetStateAction<ModalState<ModelModalType> | null>>
|
setShowModelModal: Dispatch<SetStateAction<ModalState<ModelModalType> | null>>
|
||||||
}>({
|
}>({
|
||||||
@ -50,7 +50,7 @@ const ModalContext = createContext<{
|
|||||||
setShowExternalDataToolModal: () => { },
|
setShowExternalDataToolModal: () => { },
|
||||||
setShowPricingModal: () => { },
|
setShowPricingModal: () => { },
|
||||||
setShowAnnotationFullModal: () => { },
|
setShowAnnotationFullModal: () => { },
|
||||||
setShowModelModal: () => {},
|
setShowModelModal: () => { },
|
||||||
})
|
})
|
||||||
|
|
||||||
export const useModalContext = () => useContext(ModalContext)
|
export const useModalContext = () => useContext(ModalContext)
|
||||||
|
@ -28,6 +28,7 @@ const translation = {
|
|||||||
talkToSales: 'Talk to Sales',
|
talkToSales: 'Talk to Sales',
|
||||||
modelProviders: 'Model Providers',
|
modelProviders: 'Model Providers',
|
||||||
teamMembers: 'Team Members',
|
teamMembers: 'Team Members',
|
||||||
|
annotationQuota: 'Annotation Quota',
|
||||||
buildApps: 'Build Apps',
|
buildApps: 'Build Apps',
|
||||||
vectorSpace: 'Vector Space',
|
vectorSpace: 'Vector Space',
|
||||||
vectorSpaceBillingTooltip: 'Each 1MB can store about 1.2million characters of vectorized data(estimated using OpenAI Embeddings, varies across models).',
|
vectorSpaceBillingTooltip: 'Each 1MB can store about 1.2million characters of vectorized data(estimated using OpenAI Embeddings, varies across models).',
|
||||||
|
@ -29,6 +29,7 @@ const translation = {
|
|||||||
modelProviders: '支持的模型提供商',
|
modelProviders: '支持的模型提供商',
|
||||||
teamMembers: '团队成员',
|
teamMembers: '团队成员',
|
||||||
buildApps: '构建应用程序数',
|
buildApps: '构建应用程序数',
|
||||||
|
annotationQuota: '标注回复数',
|
||||||
vectorSpace: '向量空间',
|
vectorSpace: '向量空间',
|
||||||
vectorSpaceTooltip: '向量空间是 LLMs 理解您的数据所需的长期记忆系统。',
|
vectorSpaceTooltip: '向量空间是 LLMs 理解您的数据所需的长期记忆系统。',
|
||||||
vectorSpaceBillingTooltip: '向量存储是将知识库向量化处理后为让 LLMs 理解数据而使用的长期记忆存储,1MB 大约能满足1.2 million character 的向量化后数据存储(以 OpenAI Embedding 模型估算,不同模型计算方式有差异)。在向量化过程中,实际的压缩或尺寸减小取决于内容的复杂性和冗余性。',
|
vectorSpaceBillingTooltip: '向量存储是将知识库向量化处理后为让 LLMs 理解数据而使用的长期记忆存储,1MB 大约能满足1.2 million character 的向量化后数据存储(以 OpenAI Embedding 模型估算,不同模型计算方式有差异)。在向量化过程中,实际的压缩或尺寸减小取决于内容的复杂性和冗余性。',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user