| @@ -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> | |||
| @@ -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 FilePlus02 } from './FilePlus02' | |||
| export { default as FileText } from './FileText' | |||
| export { default as FileUpload } from './FileUpload' | |||
| export { default as Folder } from './Folder' | |||
| @@ -86,11 +86,13 @@ export const defaultPlan = { | |||
| buildApps: 1, | |||
| teamMembers: 1, | |||
| annotatedResponse: 1, | |||
| documentsUploadQuota: 1, | |||
| }, | |||
| total: { | |||
| vectorSpace: 10, | |||
| buildApps: 10, | |||
| teamMembers: 1, | |||
| annotatedResponse: 10, | |||
| documentsUploadQuota: 50, | |||
| }, | |||
| } | |||
| @@ -7,7 +7,11 @@ import { Plan } from '../type' | |||
| import VectorSpaceInfo from '../usage-info/vector-space-info' | |||
| import AppsInfo from '../usage-info/apps-info' | |||
| 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 UsageInfo from '@/app/components/billing/usage-info' | |||
| const typeStyle = { | |||
| [Plan.sandbox]: { | |||
| @@ -41,6 +45,11 @@ const PlanComp: FC<Props> = ({ | |||
| type, | |||
| } = plan | |||
| const { | |||
| usage, | |||
| total, | |||
| } = plan | |||
| const isInHeader = loc === 'header' | |||
| return ( | |||
| @@ -76,8 +85,30 @@ const PlanComp: FC<Props> = ({ | |||
| {/* Plan detail */} | |||
| <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' /> | |||
| <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 && ( | |||
| <UpgradeBtn | |||
| className='flex-shrink-0 my-3' | |||
| @@ -28,7 +28,7 @@ export type PlanInfo = { | |||
| annotatedResponse: number | |||
| } | |||
| export type UsagePlanInfo = Pick<PlanInfo, 'vectorSpace' | 'buildApps' | 'teamMembers' | 'annotatedResponse'> | |||
| export type UsagePlanInfo = Pick<PlanInfo, 'vectorSpace' | 'buildApps' | 'teamMembers' | 'annotatedResponse' | 'documentsUploadQuota'> | |||
| export enum DocumentProcessingPriority { | |||
| standard = 'standard', | |||
| @@ -59,6 +59,10 @@ export type CurrentPlanInfoBackend = { | |||
| size: number | |||
| limit: number // total. 0 means unlimited | |||
| } | |||
| documents_upload_quota: { | |||
| size: number | |||
| limit: number // total. 0 means unlimited | |||
| } | |||
| docs_processing: DocumentProcessingPriority | |||
| can_replace_logo: boolean | |||
| } | |||
| @@ -16,12 +16,14 @@ export const parseCurrentPlan = (data: CurrentPlanInfoBackend) => { | |||
| buildApps: data.apps?.size || 0, | |||
| teamMembers: data.members.size, | |||
| annotatedResponse: data.annotation_quota_limit.size, | |||
| documentsUploadQuota: data.documents_upload_quota.size, | |||
| }, | |||
| total: { | |||
| vectorSpace: parseLimit(data.vector_space.limit), | |||
| buildApps: parseLimit(data.apps?.limit) || 0, | |||
| teamMembers: parseLimit(data.members.limit), | |||
| annotatedResponse: parseLimit(data.annotation_quota_limit.limit), | |||
| documentsUploadQuota: parseLimit(data.documents_upload_quota.limit), | |||
| }, | |||
| } | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| 'use client' | |||
| import { useEffect, useRef, useState } from 'react' | |||
| import { useCallback, useEffect } from 'react' | |||
| import Link from 'next/link' | |||
| import { useBoolean, useClickAway } from 'ahooks' | |||
| import { useBoolean } from 'ahooks' | |||
| import { useSelectedLayoutSegment } from 'next/navigation' | |||
| import { Bars3Icon } from '@heroicons/react/20/solid' | |||
| import HeaderBillingBtn from '../billing/header-billing-btn' | |||
| @@ -15,9 +15,9 @@ import GithubStar from './github-star' | |||
| import { WorkspaceProvider } from '@/context/workspace-context' | |||
| import { useAppContext } from '@/context/app-context' | |||
| import LogoSite from '@/app/components/base/logo/logo-site' | |||
| import PlanComp from '@/app/components/billing/plan' | |||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | |||
| import { useProviderContext } from '@/context/provider-context' | |||
| import { useModalContext } from '@/context/modal-context' | |||
| const navClassName = ` | |||
| flex items-center relative mr-0 sm:mr-3 px-3 h-8 rounded-xl | |||
| @@ -26,18 +26,21 @@ const navClassName = ` | |||
| ` | |||
| const Header = () => { | |||
| const { isCurrentWorkspaceManager, langeniusVersionInfo } = useAppContext() | |||
| const [showUpgradePanel, setShowUpgradePanel] = useState(false) | |||
| const upgradeBtnRef = useRef<HTMLElement>(null) | |||
| useClickAway(() => { | |||
| setShowUpgradePanel(false) | |||
| }, upgradeBtnRef) | |||
| const { isCurrentWorkspaceManager } = useAppContext() | |||
| const selectedSegment = useSelectedLayoutSegment() | |||
| const media = useBreakpoints() | |||
| const isMobile = media === MediaType.mobile | |||
| 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(() => { | |||
| hideNavMenu() | |||
| @@ -79,15 +82,7 @@ const Header = () => { | |||
| <EnvNav /> | |||
| {enableBilling && ( | |||
| <div className='mr-3 select-none'> | |||
| <HeaderBillingBtn onClick={() => setShowUpgradePanel(true)} /> | |||
| {showUpgradePanel && ( | |||
| <div | |||
| ref={upgradeBtnRef as any} | |||
| className='fixed z-10 top-12 right-1 w-[360px]' | |||
| > | |||
| <PlanComp loc='header' /> | |||
| </div> | |||
| )} | |||
| <HeaderBillingBtn onClick={handlePlanClick} /> | |||
| </div> | |||
| )} | |||
| <WorkspaceProvider> | |||
| @@ -40,7 +40,7 @@ const ModalContext = createContext<{ | |||
| setShowApiBasedExtensionModal: Dispatch<SetStateAction<ModalState<ApiBasedExtension> | null>> | |||
| setShowModerationSettingModal: Dispatch<SetStateAction<ModalState<ModerationConfig> | null>> | |||
| setShowExternalDataToolModal: Dispatch<SetStateAction<ModalState<ExternalDataTool> | null>> | |||
| setShowPricingModal: Dispatch<SetStateAction<any>> | |||
| setShowPricingModal: () => void | |||
| setShowAnnotationFullModal: () => void | |||
| setShowModelModal: Dispatch<SetStateAction<ModalState<ModelModalType> | null>> | |||
| }>({ | |||
| @@ -50,7 +50,7 @@ const ModalContext = createContext<{ | |||
| setShowExternalDataToolModal: () => { }, | |||
| setShowPricingModal: () => { }, | |||
| setShowAnnotationFullModal: () => { }, | |||
| setShowModelModal: () => {}, | |||
| setShowModelModal: () => { }, | |||
| }) | |||
| export const useModalContext = () => useContext(ModalContext) | |||
| @@ -28,6 +28,7 @@ const translation = { | |||
| talkToSales: 'Talk to Sales', | |||
| modelProviders: 'Model Providers', | |||
| teamMembers: 'Team Members', | |||
| annotationQuota: 'Annotation Quota', | |||
| buildApps: 'Build Apps', | |||
| vectorSpace: 'Vector Space', | |||
| 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: '支持的模型提供商', | |||
| teamMembers: '团队成员', | |||
| buildApps: '构建应用程序数', | |||
| annotationQuota: '标注回复数', | |||
| vectorSpace: '向量空间', | |||
| vectorSpaceTooltip: '向量空间是 LLMs 理解您的数据所需的长期记忆系统。', | |||
| vectorSpaceBillingTooltip: '向量存储是将知识库向量化处理后为让 LLMs 理解数据而使用的长期记忆存储,1MB 大约能满足1.2 million character 的向量化后数据存储(以 OpenAI Embedding 模型估算,不同模型计算方式有差异)。在向量化过程中,实际的压缩或尺寸减小取决于内容的复杂性和冗余性。', | |||