From adacd01f827187432c1270acceb1332ea6433cc8 Mon Sep 17 00:00:00 2001 From: NFish Date: Mon, 30 Dec 2024 13:39:26 +0800 Subject: [PATCH 001/393] Feat: support account deletion (#10008) --- web/app/(commonLayout)/layout.tsx | 27 +- web/app/account/account-page/index.tsx | 32 +- .../delete-account/components/check-email.tsx | 48 +++ .../delete-account/components/feed-back.tsx | 68 +++++ .../components/verify-email.tsx | 55 ++++ web/app/account/delete-account/index.tsx | 44 +++ web/app/account/delete-account/state.tsx | 39 +++ web/app/components/base/dialog/index.tsx | 10 +- .../account-page/index.module.css | 9 - .../account-setting/account-page/index.tsx | 282 ------------------ web/app/layout.tsx | 5 +- web/i18n/en-US/common.ts | 17 +- web/i18n/zh-Hans/common.ts | 17 +- web/service/common.ts | 9 + 14 files changed, 316 insertions(+), 346 deletions(-) create mode 100644 web/app/account/delete-account/components/check-email.tsx create mode 100644 web/app/account/delete-account/components/feed-back.tsx create mode 100644 web/app/account/delete-account/components/verify-email.tsx create mode 100644 web/app/account/delete-account/index.tsx create mode 100644 web/app/account/delete-account/state.tsx delete mode 100644 web/app/components/header/account-setting/account-page/index.module.css delete mode 100644 web/app/components/header/account-setting/account-page/index.tsx diff --git a/web/app/(commonLayout)/layout.tsx b/web/app/(commonLayout)/layout.tsx index f0f7e0321d..af36d4d961 100644 --- a/web/app/(commonLayout)/layout.tsx +++ b/web/app/(commonLayout)/layout.tsx @@ -8,27 +8,24 @@ import Header from '@/app/components/header' import { EventEmitterContextProvider } from '@/context/event-emitter' import { ProviderContextProvider } from '@/context/provider-context' import { ModalContextProvider } from '@/context/modal-context' -import { TanstackQueryIniter } from '@/context/query-client' const Layout = ({ children }: { children: ReactNode }) => { return ( <> - - - - - - -
- - {children} - - - - - + + + + + +
+ + {children} + + + + ) diff --git a/web/app/account/account-page/index.tsx b/web/app/account/account-page/index.tsx index c7af05793f..4435019561 100644 --- a/web/app/account/account-page/index.tsx +++ b/web/app/account/account-page/index.tsx @@ -3,11 +3,11 @@ import { useState } from 'react' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' +import DeleteAccount from '../delete-account' import s from './index.module.css' import Collapse from '@/app/components/header/account-setting/collapse' import type { IItem } from '@/app/components/header/account-setting/collapse' import Modal from '@/app/components/base/modal' -import Confirm from '@/app/components/base/confirm' import Button from '@/app/components/base/button' import { updateUserProfile } from '@/service/common' import { useAppContext } from '@/context/app-context' @@ -296,37 +296,9 @@ export default function AccountPage() { } { showDeleteAccountModal && ( - setShowDeleteAccountModal(false)} onConfirm={() => setShowDeleteAccountModal(false)} - showCancel={false} - type='warning' - title={t('common.account.delete')} - content={ - <> -
- {t('common.account.deleteTip')} -
- -
{`${t('common.account.delete')}: ${userProfile.email}`}
- - } - confirmText={t('common.operation.ok') as string} /> ) } diff --git a/web/app/account/delete-account/components/check-email.tsx b/web/app/account/delete-account/components/check-email.tsx new file mode 100644 index 0000000000..84ea8a4c24 --- /dev/null +++ b/web/app/account/delete-account/components/check-email.tsx @@ -0,0 +1,48 @@ +'use client' +import { useTranslation } from 'react-i18next' +import { useCallback, useState } from 'react' +import Link from 'next/link' +import { useSendDeleteAccountEmail } from '../state' +import { useAppContext } from '@/context/app-context' +import Input from '@/app/components/base/input' +import Button from '@/app/components/base/button' + +type DeleteAccountProps = { + onCancel: () => void + onConfirm: () => void +} + +export default function CheckEmail(props: DeleteAccountProps) { + const { t } = useTranslation() + const { userProfile } = useAppContext() + const [userInputEmail, setUserInputEmail] = useState('') + + const { isPending: isSendingEmail, mutateAsync: getDeleteEmailVerifyCode } = useSendDeleteAccountEmail() + + const handleConfirm = useCallback(async () => { + try { + const ret = await getDeleteEmailVerifyCode() + if (ret.result === 'success') + props.onConfirm() + } + catch (error) { console.error(error) } + }, [getDeleteEmailVerifyCode, props]) + + return <> +
+ {t('common.account.deleteTip')} +
+
+ {t('common.account.deletePrivacyLinkTip')} + {t('common.account.deletePrivacyLink')} +
+ + { + setUserInputEmail(e.target.value) + }} /> +
+ + +
+ +} diff --git a/web/app/account/delete-account/components/feed-back.tsx b/web/app/account/delete-account/components/feed-back.tsx new file mode 100644 index 0000000000..1d01c69d94 --- /dev/null +++ b/web/app/account/delete-account/components/feed-back.tsx @@ -0,0 +1,68 @@ +'use client' +import { useTranslation } from 'react-i18next' +import { useCallback, useState } from 'react' +import { useRouter } from 'next/navigation' +import { useDeleteAccountFeedback } from '../state' +import { useAppContext } from '@/context/app-context' +import Button from '@/app/components/base/button' +import CustomDialog from '@/app/components/base/dialog' +import Textarea from '@/app/components/base/textarea' +import Toast from '@/app/components/base/toast' +import { logout } from '@/service/common' + +type DeleteAccountProps = { + onCancel: () => void + onConfirm: () => void +} + +export default function FeedBack(props: DeleteAccountProps) { + const { t } = useTranslation() + const { userProfile } = useAppContext() + const router = useRouter() + const [userFeedback, setUserFeedback] = useState('') + const { isPending, mutateAsync: sendFeedback } = useDeleteAccountFeedback() + + const handleSuccess = useCallback(async () => { + try { + await logout({ + url: '/logout', + params: {}, + }) + localStorage.removeItem('refresh_token') + localStorage.removeItem('console_token') + router.push('/signin') + Toast.notify({ type: 'info', message: t('common.account.deleteSuccessTip') }) + } + catch (error) { console.error(error) } + }, [router, t]) + + const handleSubmit = useCallback(async () => { + try { + await sendFeedback({ feedback: userFeedback, email: userProfile.email }) + props.onConfirm() + await handleSuccess() + } + catch (error) { console.error(error) } + }, [handleSuccess, userFeedback, sendFeedback, userProfile, props]) + + const handleSkip = useCallback(() => { + props.onCancel() + handleSuccess() + }, [handleSuccess, props]) + return + + - - ) - : ( -
- )} - {renderQuestions()} - ) : ( -
{t('appDebug.openingStatement.noDataPlaceHolder')}
- )} - - {isShowConfirmAddVar && ( - - )} - - - - ) -} -export default React.memo(OpeningStatement) diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 3a5ee386bb..1d37f73724 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -19,6 +19,7 @@ import { } from '@/app/components/app/configuration/debug/hooks' import type { ModelAndParameter } from '@/app/components/app/configuration/debug/types' import Button from '@/app/components/base/button' +import Divider from '@/app/components/base/divider' import Loading from '@/app/components/base/loading' import AppPublisher from '@/app/components/app/app-publisher/features-wrapper' import type { @@ -59,7 +60,7 @@ import { useTextGenerationCurrentProviderAndModelAndModelList, } from '@/app/components/header/account-setting/model-provider-page/hooks' import { fetchCollectionList } from '@/service/tools' -import { type Collection } from '@/app/components/tools/types' +import type { Collection } from '@/app/components/tools/types' import { useStore as useAppStore } from '@/app/components/app/store' import { getMultipleRetrievalConfig, @@ -71,6 +72,8 @@ import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' import { SupportUploadFileTypes } from '@/app/components/workflow/types' import NewFeaturePanel from '@/app/components/base/features/new-feature-panel' import { fetchFileUploadConfig } from '@/service/common' +import { correctProvider } from '@/utils' +import PluginDependency from '@/app/components/workflow/plugin-dependency' type PublishConfig = { modelConfig: ModelConfig @@ -156,7 +159,7 @@ const Configuration: FC = () => { const setCompletionParams = (value: FormValue) => { const params = { ...value } - // eslint-disable-next-line @typescript-eslint/no-use-before-define + // eslint-disable-next-line ts/no-use-before-define if ((!params.stop || params.stop.length === 0) && (modeModeTypeRef.current === ModelModeType.completion)) { params.stop = getTempStop() setTempStop([]) @@ -165,7 +168,7 @@ const Configuration: FC = () => { } const [modelConfig, doSetModelConfig] = useState({ - provider: 'openai', + provider: 'langgenius/openai/openai', model_id: 'gpt-3.5-turbo', mode: ModelModeType.unset, configs: { @@ -188,7 +191,7 @@ const Configuration: FC = () => { const isAgent = mode === 'agent-chat' - const isOpenAI = modelConfig.provider === 'openai' + const isOpenAI = modelConfig.provider === 'langgenius/openai/openai' const [collectionList, setCollectionList] = useState([]) useEffect(() => { @@ -361,7 +364,7 @@ const Configuration: FC = () => { const [canReturnToSimpleMode, setCanReturnToSimpleMode] = useState(true) const setPromptMode = async (mode: PromptMode) => { if (mode === PromptMode.advanced) { - // eslint-disable-next-line @typescript-eslint/no-use-before-define + // eslint-disable-next-line ts/no-use-before-define await migrateToDefaultPrompt() setCanReturnToSimpleMode(true) } @@ -547,8 +550,19 @@ const Configuration: FC = () => { if (modelConfig.retriever_resource) setCitationConfig(modelConfig.retriever_resource) - if (modelConfig.annotation_reply) - setAnnotationConfig(modelConfig.annotation_reply, true) + if (modelConfig.annotation_reply) { + let annotationConfig = modelConfig.annotation_reply + if (modelConfig.annotation_reply.enabled) { + annotationConfig = { + ...modelConfig.annotation_reply, + embedding_model: { + ...modelConfig.annotation_reply.embedding_model, + embedding_provider_name: correctProvider(modelConfig.annotation_reply.embedding_model.embedding_provider_name), + }, + } + } + setAnnotationConfig(annotationConfig, true) + } if (modelConfig.sensitive_word_avoidance) setModerationConfig(modelConfig.sensitive_word_avoidance) @@ -558,7 +572,7 @@ const Configuration: FC = () => { const config = { modelConfig: { - provider: model.provider, + provider: correctProvider(model.provider), model_id: model.name, mode: model.mode, configs: { @@ -600,7 +614,6 @@ const Configuration: FC = () => { annotation_reply: modelConfig.annotation_reply, external_data_tools: modelConfig.external_data_tools, dataSets: datasets || [], - // eslint-disable-next-line multiline-ternary agentConfig: res.mode === 'agent-chat' ? { max_iteration: DEFAULT_AGENT_SETTING.max_iteration, ...modelConfig.agent_mode, @@ -611,8 +624,12 @@ const Configuration: FC = () => { }).map((tool: any) => { return { ...tool, - isDeleted: res.deleted_tools?.includes(tool.tool_name), + isDeleted: res.deleted_tools?.some((deletedTool: any) => deletedTool.id === tool.id && deletedTool.tool_name === tool.tool_name), notAuthor: collectionList.find(c => tool.provider_id === c.id)?.is_team_authorization === false, + ...(tool.provider_type === 'builtin' ? { + provider_id: correctProvider(tool.provider_name), + provider_name: correctProvider(tool.provider_name), + } : {}), } }), } : DEFAULT_AGENT_SETTING, @@ -633,6 +650,12 @@ const Configuration: FC = () => { retrieval_model: RETRIEVE_TYPE.multiWay, ...modelConfig.dataset_configs, ...retrievalConfig, + ...(retrievalConfig.reranking_model ? { + reranking_model: { + ...retrievalConfig.reranking_model, + reranking_provider_name: correctProvider(modelConfig.dataset_configs.reranking_model.reranking_provider_name), + }, + } : {}), }) setHasFetchedDetail(true) }) @@ -873,13 +896,13 @@ const Configuration: FC = () => {
{/* Header */} -
+
-
{t('appDebug.orchestrate')}
+
{t('appDebug.orchestrate')}
{isAdvancedMode && ( -
{t('appDebug.promptMode.advanced')}
+
{t('appDebug.promptMode.advanced')}
)}
@@ -915,13 +938,13 @@ const Configuration: FC = () => { debugWithMultipleModel={debugWithMultipleModel} onDebugWithMultipleModelChange={handleDebugWithMultipleModelChange} /> -
+ )} {isMobile && ( - )} { /> )} {isMobile && ( - + setShowAccountSettingModal({ payload: 'provider' })} @@ -1020,6 +1043,7 @@ const Configuration: FC = () => { onAutoAddPromptVariable={handleAddPromptVariable} /> )} + diff --git a/web/app/components/app/configuration/prompt-value-panel/index.tsx b/web/app/components/app/configuration/prompt-value-panel/index.tsx index a4aadc9576..2625f224eb 100644 --- a/web/app/components/app/configuration/prompt-value-panel/index.tsx +++ b/web/app/components/app/configuration/prompt-value-panel/index.tsx @@ -157,7 +157,7 @@ const PromptValuePanel: FC = ({ ))} {visionConfig?.enabled && (
-
{t('common.imageUploader.imageUpload')}
+
{t('common.imageUploader.imageUpload')}
void - onScoreChange: (score: number, embeddingModel?: EmbeddingModelConfig) => void -} - -export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }> = ({ - title, - tooltip, - children, -}) => { - return ( -
-
-
{title}
- {tooltip}
- } - /> -
-
{children}
-
- ) -} - -const AnnotationReplyConfig: FC = ({ - onEmbeddingChange, - onScoreChange, -}) => { - const { t } = useTranslation() - const router = useRouter() - const pathname = usePathname() - const matched = pathname.match(/\/app\/([^/]+)/) - const appId = (matched?.length && matched[1]) ? matched[1] : '' - const { - annotationConfig, - } = useContext(ConfigContext) - - const [isShowEdit, setIsShowEdit] = React.useState(false) - - return ( - <> - - } - title={t('appDebug.feature.annotation.title')} - headerRight={ -
-
{ setIsShowEdit(true) }} - > - -
- - {t('common.operation.params')} -
-
-
{ - router.push(`/app/${appId}/annotations`) - }}> -
{t('appDebug.feature.annotation.cacheManagement')}
- -
-
- } - noBodySpacing - /> - {isShowEdit && ( - { - setIsShowEdit(false) - }} - onSave={async (embeddingModel, score) => { - const annotationConfig = await fetchAnnotationConfig(appId) as AnnotationReplyConfigType - let isEmbeddingModelChanged = false - if ( - embeddingModel.embedding_model_name !== annotationConfig.embedding_model.embedding_model_name - || embeddingModel.embedding_provider_name !== annotationConfig.embedding_model.embedding_provider_name - ) { - await onEmbeddingChange(embeddingModel) - isEmbeddingModelChanged = true - } - - if (score !== annotationConfig.score_threshold) { - await updateAnnotationScore(appId, annotationConfig.id, score) - if (isEmbeddingModelChanged) - onScoreChange(score, embeddingModel) - - else - onScoreChange(score) - } - - setIsShowEdit(false) - }} - annotationConfig={annotationConfig} - /> - )} - - ) -} -export default React.memo(AnnotationReplyConfig) diff --git a/web/app/components/app/configuration/toolbox/index.tsx b/web/app/components/app/configuration/toolbox/index.tsx deleted file mode 100644 index 00ea301a42..0000000000 --- a/web/app/components/app/configuration/toolbox/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -'use client' - -import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' -import GroupName from '../base/group-name' -import Moderation from './moderation' -import Annotation from './annotation/config-param' -import type { EmbeddingModelConfig } from '@/app/components/app/annotation/type' - -export type ToolboxProps = { - showModerationSettings: boolean - showAnnotation: boolean - onEmbeddingChange: (embeddingModel: EmbeddingModelConfig) => void - onScoreChange: (score: number, embeddingModel?: EmbeddingModelConfig) => void -} - -const Toolbox: FC = ({ - showModerationSettings, - showAnnotation, - onEmbeddingChange, - onScoreChange, -}) => { - const { t } = useTranslation() - - return ( -
- - { - showModerationSettings && ( - - ) - } - { - showAnnotation && ( - - ) - } -
- ) -} -export default React.memo(Toolbox) diff --git a/web/app/components/app/configuration/tools/external-data-tool-modal.tsx b/web/app/components/app/configuration/tools/external-data-tool-modal.tsx index eefdd4514c..e1fe73ee32 100644 --- a/web/app/components/app/configuration/tools/external-data-tool-modal.tsx +++ b/web/app/components/app/configuration/tools/external-data-tool-modal.tsx @@ -21,13 +21,13 @@ import { useToastContext } from '@/app/components/base/toast' import AppIcon from '@/app/components/base/app-icon' const systemTypes = ['api'] -type ExternalDataToolModalProps = { +interface ExternalDataToolModalProps { data: ExternalDataTool onCancel: () => void onSave: (externalDataTool: ExternalDataTool) => void onValidateBeforeSave?: (externalDataTool: ExternalDataTool) => boolean } -type Provider = { +interface Provider { key: string name: string form_schema?: CodeBasedExtensionItem['form_schema'] diff --git a/web/app/components/app/create-app-dialog/app-list/index.tsx b/web/app/components/app/create-app-dialog/app-list/index.tsx index f158f21d99..a545774f2c 100644 --- a/web/app/components/app/create-app-dialog/app-list/index.tsx +++ b/web/app/components/app/create-app-dialog/app-list/index.tsx @@ -27,6 +27,7 @@ import { getRedirection } from '@/utils/app-redirection' import Input from '@/app/components/base/input' import type { AppMode } from '@/types/app' import { DSLImportMode } from '@/models/app' +import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' type AppsProps = { onSuccess?: () => void @@ -119,6 +120,7 @@ const Apps = ({ const [currApp, setCurrApp] = React.useState(null) const [isShowCreateModal, setIsShowCreateModal] = React.useState(false) + const { handleCheckPluginDependencies } = usePluginDependencies() const onCreate: CreateAppModalProps['onConfirm'] = async ({ name, icon_type, @@ -146,6 +148,8 @@ const Apps = ({ }) if (onSuccess) onSuccess() + if (app.app_id) + await handleCheckPluginDependencies(app.app_id) localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') getRedirection(isCurrentWorkspaceEditor, { id: app.app_id }, push) } diff --git a/web/app/components/app/create-from-dsl-modal/index.tsx b/web/app/components/app/create-from-dsl-modal/index.tsx index ce06b113bc..26e175eb56 100644 --- a/web/app/components/app/create-from-dsl-modal/index.tsx +++ b/web/app/components/app/create-from-dsl-modal/index.tsx @@ -25,6 +25,7 @@ import AppsFull from '@/app/components/billing/apps-full-in-dialog' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { getRedirection } from '@/utils/app-redirection' import cn from '@/utils/classnames' +import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks' type CreateFromDSLModalProps = { show: boolean @@ -50,6 +51,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS const [showErrorModal, setShowErrorModal] = useState(false) const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>() const [importId, setImportId] = useState() + const { handleCheckPluginDependencies } = usePluginDependencies() const readFile = (file: File) => { const reader = new FileReader() @@ -114,6 +116,8 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS children: status === DSLImportStatus.COMPLETED_WITH_WARNINGS && t('app.newApp.appCreateDSLWarning'), }) localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') + if (app_id) + await handleCheckPluginDependencies(app_id) getRedirection(isCurrentWorkspaceEditor, { id: app_id }, push) } else if (status === DSLImportStatus.PENDING) { @@ -132,6 +136,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) } } + // eslint-disable-next-line unused-imports/no-unused-vars catch (e) { notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) } @@ -158,6 +163,8 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS type: 'success', message: t('app.newApp.appCreated'), }) + if (app_id) + await handleCheckPluginDependencies(app_id) localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1') getRedirection(isCurrentWorkspaceEditor, { id: app_id }, push) } @@ -165,6 +172,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) } } + // eslint-disable-next-line unused-imports/no-unused-vars catch (e) { notify({ type: 'error', message: t('app.newApp.appCreateFailed') }) } @@ -268,7 +276,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS >
{t('app.newApp.appCreateDSLErrorTitle')}
-
+
{t('app.newApp.appCreateDSLErrorPart1')}
{t('app.newApp.appCreateDSLErrorPart2')}

diff --git a/web/app/components/app/duplicate-modal/index.tsx b/web/app/components/app/duplicate-modal/index.tsx index bcad1c24f2..25a5cbf6c1 100644 --- a/web/app/components/app/duplicate-modal/index.tsx +++ b/web/app/components/app/duplicate-modal/index.tsx @@ -13,7 +13,7 @@ import { useProviderContext } from '@/context/provider-context' import AppsFull from '@/app/components/billing/apps-full-in-dialog' import type { AppIconType } from '@/types/app' -export type DuplicateAppModalProps = { +export interface DuplicateAppModalProps { appName: string icon_type: AppIconType | null icon: string diff --git a/web/app/components/app/log/list.tsx b/web/app/components/app/log/list.tsx index 383aeb1492..2862eebfa7 100644 --- a/web/app/components/app/log/list.tsx +++ b/web/app/components/app/log/list.tsx @@ -79,6 +79,9 @@ const HandThumbIconWithCount: FC<{ count: number; iconType: 'up' | 'down' }> = ( } const statusTdRender = (statusCount: StatusCount) => { + if (!statusCount) + return null + if (statusCount.partial_success + statusCount.failed === 0) { return (
diff --git a/web/app/components/app/overview/apikey-info-panel/index.tsx b/web/app/components/app/overview/apikey-info-panel/index.tsx index 661a88e823..2ca098a313 100644 --- a/web/app/components/app/overview/apikey-info-panel/index.tsx +++ b/web/app/components/app/overview/apikey-info-panel/index.tsx @@ -27,8 +27,8 @@ const APIKeyInfoPanel: FC = () => { return null return ( -
-
+
+
{isCloud && } {isCloud ? ( @@ -42,11 +42,11 @@ const APIKeyInfoPanel: FC = () => { )}
{isCloud && ( -
{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}
+
{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}
)}
) diff --git a/web/app/components/app/overview/apikey-info-panel/progress/index.tsx b/web/app/components/app/overview/apikey-info-panel/progress/index.tsx deleted file mode 100644 index 3a4accbb43..0000000000 --- a/web/app/components/app/overview/apikey-info-panel/progress/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import s from './style.module.css' -import cn from '@/utils/classnames' - -export type IProgressProps = { - className?: string - value: number // percent -} - -const Progress: FC = ({ - className, - value, -}) => { - const exhausted = value === 100 - return ( -
-
- {Array(10).fill(0).map((i, k) => ( -
- ))} -
- ) -} -export default React.memo(Progress) diff --git a/web/app/components/app/overview/apikey-info-panel/progress/style.module.css b/web/app/components/app/overview/apikey-info-panel/progress/style.module.css deleted file mode 100644 index 94c3ef45cf..0000000000 --- a/web/app/components/app/overview/apikey-info-panel/progress/style.module.css +++ /dev/null @@ -1,16 +0,0 @@ -.bar { - background: linear-gradient(90deg, rgba(41, 112, 255, 0.9) 0%, rgba(21, 94, 239, 0.9) 100%); -} - -.bar-error { - background: linear-gradient(90deg, rgba(240, 68, 56, 0.72) 0%, rgba(217, 45, 32, 0.9) 100%); -} - -.bar-item { - width: 10%; - border-right: 1px solid rgba(255, 255, 255, 0.5); -} - -.bar-item:last-of-type { - border-right: 0; -} \ No newline at end of file diff --git a/web/app/components/app/overview/appChart.tsx b/web/app/components/app/overview/appChart.tsx index 43b1cb6afe..3d8de9077a 100644 --- a/web/app/components/app/overview/appChart.tsx +++ b/web/app/components/app/overview/appChart.tsx @@ -215,8 +215,8 @@ const Chart: React.FC = ({ return `
${params.name}
${valueFormatter((params.data as any)[yField])} ${!CHART_TYPE_CONFIG[chartType].showTokens - ? '' - : ` + ? '' + : ` ( ~$${get(params.data, 'total_price', 0)} ) @@ -230,7 +230,7 @@ const Chart: React.FC = ({ const sumData = isAvg ? (sum(yData) / yData.length) : sum(yData) return ( -
+
@@ -241,11 +241,11 @@ const Chart: React.FC = ({ type={!CHART_TYPE_CONFIG[chartType].showTokens ? '' : {t('appOverview.analysis.tokenUsage.consumed')} Tokens - ( - ~{sum(statistics.map(item => parseFloat(get(item, 'total_price', '0')))).toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 4 })} - ) + ( + ~{sum(statistics.map(item => Number.parseFloat(get(item, 'total_price', '0')))).toLocaleString('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 4 })} + ) } - textStyle={{ main: `!text-3xl !font-normal ${sumData === 0 ? '!text-gray-300' : ''}` }} /> + textStyle={{ main: `!text-3xl !font-normal ${sumData === 0 ? '!text-text-quaternary' : ''}` }} />
diff --git a/web/app/components/app/overview/customize/index.tsx b/web/app/components/app/overview/customize/index.tsx index d53aa00a6f..2925ba41ee 100644 --- a/web/app/components/app/overview/customize/index.tsx +++ b/web/app/components/app/overview/customize/index.tsx @@ -21,7 +21,7 @@ type IShareLinkProps = { } const StepNum: FC<{ children: React.ReactNode }> = ({ children }) => -
+
{children}
@@ -54,27 +54,27 @@ const CustomizeModal: FC = ({ className='!max-w-2xl w-[640px]' closable={true} > -
- {t(`${prefixCustomize}.way`)} 1 -

{t(`${prefixCustomize}.way1.name`)}

+
+ {t(`${prefixCustomize}.way`)} 1 +

{t(`${prefixCustomize}.way1.name`)}

1
-
{t(`${prefixCustomize}.way1.step1`)}
-
{t(`${prefixCustomize}.way1.step1Tip`)}
+
{t(`${prefixCustomize}.way1.step1`)}
+
{t(`${prefixCustomize}.way1.step1Tip`)}
- +
2
-
{t(`${prefixCustomize}.way1.step3`)}
-
{t(`${prefixCustomize}.way1.step2Tip`)}
+
{t(`${prefixCustomize}.way1.step3`)}
+
{t(`${prefixCustomize}.way1.step2Tip`)}
@@ -83,9 +83,9 @@ const CustomizeModal: FC = ({
3
-
{t(`${prefixCustomize}.way1.step3`)}
-
{t(`${prefixCustomize}.way1.step3Tip`)}
-
+          
{t(`${prefixCustomize}.way1.step3`)}
+
{t(`${prefixCustomize}.way1.step3Tip`)}
+
             NEXT_PUBLIC_APP_ID={`'${appId}'`} 
NEXT_PUBLIC_APP_KEY={'\'\''}
NEXT_PUBLIC_API_URL={`'${api_base_url}'`} @@ -94,9 +94,9 @@ const CustomizeModal: FC = ({
-
- {t(`${prefixCustomize}.way`)} 2 -

{t(`${prefixCustomize}.way2.name`)}

+
+ {t(`${prefixCustomize}.way`)} 2 +

{t(`${prefixCustomize}.way2.name`)}

diff --git a/web/app/components/app/overview/embedded/index.tsx b/web/app/components/app/overview/embedded/index.tsx index b71a3c3fdf..06b06e28a8 100644 --- a/web/app/components/app/overview/embedded/index.tsx +++ b/web/app/components/app/overview/embedded/index.tsx @@ -1,15 +1,19 @@ import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import { + RiClipboardFill, + RiClipboardLine, +} from '@remixicon/react' import copy from 'copy-to-clipboard' import style from './style.module.css' -import cn from '@/utils/classnames' import Modal from '@/app/components/base/modal' -import copyStyle from '@/app/components/base/copy-btn/style.module.css' import Tooltip from '@/app/components/base/tooltip' import { useAppContext } from '@/context/app-context' import { IS_CE_EDITION } from '@/config' import type { SiteInfo } from '@/models/share' import { useThemeContext } from '@/app/components/base/chat/embedded-chatbot/theme/theme-context' +import ActionButton from '@/app/components/base/action-button' +import cn from '@/utils/classnames' type Props = { siteInfo?: SiteInfo @@ -35,12 +39,12 @@ const OPTION_MAP = { `