mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-14 01:45:55 +08:00
Chore: update app detail panel (#13337)
This commit is contained in:
parent
ca19bd31d4
commit
ae6f67420c
@ -161,9 +161,9 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(s.app, 'flex', 'overflow-hidden')}>
|
<div className={cn(s.app, 'flex relative', 'overflow-hidden')}>
|
||||||
{appDetail && (
|
{appDetail && (
|
||||||
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background} desc={appDetail.mode} navigation={navigation} />
|
<AppSideBar title={appDetail.name} icon={appDetail.icon} icon_background={appDetail.icon_background as string} desc={appDetail.mode} navigation={navigation} />
|
||||||
)}
|
)}
|
||||||
<div className="bg-components-panel-bg grow overflow-hidden">
|
<div className="bg-components-panel-bg grow overflow-hidden">
|
||||||
{children}
|
{children}
|
||||||
|
@ -24,9 +24,11 @@ import AppContext from '@/context/app-context'
|
|||||||
|
|
||||||
export type ICardViewProps = {
|
export type ICardViewProps = {
|
||||||
appId: string
|
appId: string
|
||||||
|
isInPanel?: boolean
|
||||||
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardView: FC<ICardViewProps> = ({ appId }) => {
|
const CardView: FC<ICardViewProps> = ({ appId, isInPanel, className }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
const appDetail = useAppStore(state => state.appDetail)
|
const appDetail = useAppStore(state => state.appDetail)
|
||||||
@ -120,10 +122,11 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
|
|||||||
return <Loading />
|
return <Loading />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6">
|
<div className={className || 'grid gap-6 grid-cols-1 xl:grid-cols-2 w-full mb-6'}>
|
||||||
<AppCard
|
<AppCard
|
||||||
appInfo={appDetail}
|
appInfo={appDetail}
|
||||||
cardType="webapp"
|
cardType="webapp"
|
||||||
|
isInPanel={isInPanel}
|
||||||
onChangeStatus={onChangeSiteStatus}
|
onChangeStatus={onChangeSiteStatus}
|
||||||
onGenerateCode={onGenerateCode}
|
onGenerateCode={onGenerateCode}
|
||||||
onSaveSiteConfig={onSaveSiteConfig}
|
onSaveSiteConfig={onSaveSiteConfig}
|
||||||
@ -131,6 +134,7 @@ const CardView: FC<ICardViewProps> = ({ appId }) => {
|
|||||||
<AppCard
|
<AppCard
|
||||||
cardType="api"
|
cardType="api"
|
||||||
appInfo={appDetail}
|
appInfo={appDetail}
|
||||||
|
isInPanel={isInPanel}
|
||||||
onChangeStatus={onChangeApiStatus}
|
onChangeStatus={onChangeApiStatus}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,8 +31,6 @@ const ApiServer: FC<ApiServerProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<SecretKeyButton
|
<SecretKeyButton
|
||||||
className='flex-shrink-0 !h-8 bg-white'
|
className='flex-shrink-0 !h-8 bg-white'
|
||||||
textCls='!text-gray-700 font-medium'
|
|
||||||
iconCls='stroke-[1.2px]'
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import { useContext, useContextSelector } from 'use-context-selector'
|
import { useContext, useContextSelector } from 'use-context-selector'
|
||||||
import { RiArrowDownSLine } from '@remixicon/react'
|
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
|
import {
|
||||||
|
RiDeleteBinLine,
|
||||||
|
RiEditLine,
|
||||||
|
RiEqualizer2Line,
|
||||||
|
RiFileCopy2Line,
|
||||||
|
RiFileDownloadLine,
|
||||||
|
RiFileUploadLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
import AppIcon from '../base/app-icon'
|
import AppIcon from '../base/app-icon'
|
||||||
import SwitchAppModal from '../app/switch-app-modal'
|
import SwitchAppModal from '../app/switch-app-modal'
|
||||||
import s from './style.module.css'
|
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import {
|
|
||||||
PortalToFollowElem,
|
|
||||||
PortalToFollowElemContent,
|
|
||||||
PortalToFollowElemTrigger,
|
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
|
||||||
import Divider from '@/app/components/base/divider'
|
|
||||||
import Confirm from '@/app/components/base/confirm'
|
import Confirm from '@/app/components/base/confirm'
|
||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
import { ToastContext } from '@/app/components/base/toast'
|
import { ToastContext } from '@/app/components/base/toast'
|
||||||
@ -22,8 +22,6 @@ import { copyApp, deleteApp, exportAppConfig, updateAppInfo } from '@/service/ap
|
|||||||
import DuplicateAppModal from '@/app/components/app/duplicate-modal'
|
import DuplicateAppModal from '@/app/components/app/duplicate-modal'
|
||||||
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
|
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
|
||||||
import CreateAppModal from '@/app/components/explore/create-app-modal'
|
import CreateAppModal from '@/app/components/explore/create-app-modal'
|
||||||
import { AiText, ChatBot, CuteRobot } from '@/app/components/base/icons/src/vender/solid/communication'
|
|
||||||
import { Route } from '@/app/components/base/icons/src/vender/solid/mapsAndTravel'
|
|
||||||
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
|
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
|
||||||
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||||
import { getRedirection } from '@/utils/app-redirection'
|
import { getRedirection } from '@/utils/app-redirection'
|
||||||
@ -31,6 +29,9 @@ import UpdateDSLModal from '@/app/components/workflow/update-dsl-modal'
|
|||||||
import type { EnvironmentVariable } from '@/app/components/workflow/types'
|
import type { EnvironmentVariable } from '@/app/components/workflow/types'
|
||||||
import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal'
|
import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal'
|
||||||
import { fetchWorkflowDraft } from '@/service/workflow'
|
import { fetchWorkflowDraft } from '@/service/workflow'
|
||||||
|
import ContentDialog from '@/app/components/base/content-dialog'
|
||||||
|
import Button from '@/app/components/base/button'
|
||||||
|
import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/cardView'
|
||||||
|
|
||||||
export type IAppInfoProps = {
|
export type IAppInfoProps = {
|
||||||
expand: boolean
|
expand: boolean
|
||||||
@ -47,7 +48,6 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
|||||||
const [showEditModal, setShowEditModal] = useState(false)
|
const [showEditModal, setShowEditModal] = useState(false)
|
||||||
const [showDuplicateModal, setShowDuplicateModal] = useState(false)
|
const [showDuplicateModal, setShowDuplicateModal] = useState(false)
|
||||||
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
|
||||||
const [showSwitchTip, setShowSwitchTip] = useState<string>('')
|
|
||||||
const [showSwitchModal, setShowSwitchModal] = useState<boolean>(false)
|
const [showSwitchModal, setShowSwitchModal] = useState<boolean>(false)
|
||||||
const [showImportDSLModal, setShowImportDSLModal] = useState<boolean>(false)
|
const [showImportDSLModal, setShowImportDSLModal] = useState<boolean>(false)
|
||||||
const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
|
const [secretEnvList, setSecretEnvList] = useState<EnvironmentVariable[]>([])
|
||||||
@ -183,291 +183,199 @@ const AppInfo = ({ expand }: IAppInfoProps) => {
|
|||||||
return null
|
return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PortalToFollowElem
|
<div>
|
||||||
open={open}
|
<button
|
||||||
onOpenChange={setOpen}
|
onClick={() => {
|
||||||
placement='bottom-start'
|
if (isCurrentWorkspaceEditor)
|
||||||
offset={4}
|
setOpen(v => !v)
|
||||||
>
|
}}
|
||||||
<div className='relative'>
|
className='block w-full'
|
||||||
<PortalToFollowElemTrigger
|
>
|
||||||
onClick={() => {
|
<div className={cn('flex rounded-lg', expand ? 'p-2 pb-2.5 flex-col gap-2' : 'p-1 gap-1 justify-center items-start', open && 'bg-state-base-hover', isCurrentWorkspaceEditor && 'hover:bg-state-base-hover cursor-pointer')}>
|
||||||
if (isCurrentWorkspaceEditor)
|
<div className={`flex items-center self-stretch ${expand ? 'justify-between' : 'flex-col gap-1'}`}>
|
||||||
setOpen(v => !v)
|
<AppIcon
|
||||||
}}
|
size={expand ? 'large' : 'small'}
|
||||||
className='block'
|
iconType={appDetail.icon_type}
|
||||||
>
|
icon={appDetail.icon}
|
||||||
<div className={cn('flex p-1 rounded-lg', open && 'bg-gray-100', isCurrentWorkspaceEditor && 'hover:bg-gray-100 cursor-pointer')}>
|
background={appDetail.icon_background}
|
||||||
<div className='relative shrink-0 mr-2'>
|
imageUrl={appDetail.icon_url}
|
||||||
<AppIcon
|
/>
|
||||||
size={expand ? 'large' : 'small'}
|
<div className='flex p-0.5 justify-center items-center rounded-md'>
|
||||||
iconType={appDetail.icon_type}
|
<div className='flex w-5 h-5 justify-center items-center'>
|
||||||
icon={appDetail.icon}
|
<RiEqualizer2Line className='w-4 h-4 text-text-tertiary' />
|
||||||
background={appDetail.icon_background}
|
|
||||||
imageUrl={appDetail.icon_url}
|
|
||||||
/>
|
|
||||||
<span className={cn(
|
|
||||||
'absolute bottom-[-3px] right-[-3px] w-4 h-4 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm',
|
|
||||||
!expand && '!w-3.5 !h-3.5 !bottom-[-2px] !right-[-2px]',
|
|
||||||
)}>
|
|
||||||
{appDetail.mode === 'advanced-chat' && (
|
|
||||||
<ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} />
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'agent-chat' && (
|
|
||||||
<CuteRobot className={cn('w-3 h-3 text-indigo-600', !expand && '!w-2.5 !h-2.5')} />
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'chat' && (
|
|
||||||
<ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} />
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'completion' && (
|
|
||||||
<AiText className={cn('w-3 h-3 text-[#0E9384]', !expand && '!w-2.5 !h-2.5')} />
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'workflow' && (
|
|
||||||
<Route className={cn('w-3 h-3 text-[#f79009]', !expand && '!w-2.5 !h-2.5')} />
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{expand && (
|
|
||||||
<div className="grow w-0">
|
|
||||||
<div className='flex justify-between items-center text-sm leading-5 font-medium text-text-secondary'>
|
|
||||||
<div className='truncate' title={appDetail.name}>{appDetail.name}</div>
|
|
||||||
{isCurrentWorkspaceEditor && <RiArrowDownSLine className='shrink-0 ml-[2px] w-3 h-3 text-gray-500' />}
|
|
||||||
</div>
|
|
||||||
<div className='flex items-center text-[10px] leading-[18px] font-medium text-gray-500 gap-1'>
|
|
||||||
{appDetail.mode === 'advanced-chat' && (
|
|
||||||
<>
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
|
||||||
<div title={t('app.types.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.advanced').toUpperCase()}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'agent-chat' && (
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'chat' && (
|
|
||||||
<>
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
|
||||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'completion' && (
|
|
||||||
<>
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
|
|
||||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'workflow' && (
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PortalToFollowElemTrigger>
|
{
|
||||||
<PortalToFollowElemContent className='z-[1002]'>
|
expand && (
|
||||||
<div className='relative w-[320px] bg-white rounded-2xl shadow-xl'>
|
<div className='flex flex-col items-start gap-1'>
|
||||||
{/* header */}
|
<div className='flex w-full'>
|
||||||
<div className={cn('flex pl-4 pt-3 pr-3', !appDetail.description && 'pb-2')}>
|
<div className='text-text-secondary system-md-semibold truncate'>{appDetail.name}</div>
|
||||||
<div className='relative shrink-0 mr-2'>
|
|
||||||
<AppIcon
|
|
||||||
size="large"
|
|
||||||
iconType={appDetail.icon_type}
|
|
||||||
icon={appDetail.icon}
|
|
||||||
background={appDetail.icon_background}
|
|
||||||
imageUrl={appDetail.icon_url}
|
|
||||||
/>
|
|
||||||
<span className='absolute bottom-[-3px] right-[-3px] w-4 h-4 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm'>
|
|
||||||
{appDetail.mode === 'advanced-chat' && (
|
|
||||||
<ChatBot className='w-3 h-3 text-[#1570EF]' />
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'agent-chat' && (
|
|
||||||
<CuteRobot className='w-3 h-3 text-indigo-600' />
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'chat' && (
|
|
||||||
<ChatBot className='w-3 h-3 text-[#1570EF]' />
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'completion' && (
|
|
||||||
<AiText className='w-3 h-3 text-[#0E9384]' />
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'workflow' && (
|
|
||||||
<Route className='w-3 h-3 text-[#f79009]' />
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className='grow w-0'>
|
|
||||||
<div title={appDetail.name} className='flex justify-between items-center text-sm leading-5 font-medium text-gray-900 truncate'>{appDetail.name}</div>
|
|
||||||
<div className='flex items-center text-[10px] leading-[18px] font-medium text-gray-500 gap-1'>
|
|
||||||
{appDetail.mode === 'advanced-chat' && (
|
|
||||||
<>
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
|
||||||
<div title={t('app.types.advanced') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.advanced').toUpperCase()}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'agent-chat' && (
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.agent').toUpperCase()}</div>
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'chat' && (
|
|
||||||
<>
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.chatbot').toUpperCase()}</div>
|
|
||||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'completion' && (
|
|
||||||
<>
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.completion').toUpperCase()}</div>
|
|
||||||
<div title={t('app.types.basic') || ''} className='px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{(t('app.types.basic').toUpperCase())}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{appDetail.mode === 'workflow' && (
|
|
||||||
<div className='shrink-0 px-1 border bg-white border-[rgba(0,0,0,0.08)] rounded-[5px] truncate'>{t('app.types.workflow').toUpperCase()}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className='text-text-tertiary system-2xs-medium-uppercase'>{appDetail.mode === 'advanced-chat' ? t('app.types.chatbot') : appDetail.mode === 'agent-chat' ? t('app.types.agent') : appDetail.mode === 'chat' ? t('app.types.chatbot') : appDetail.mode === 'completion' ? t('app.types.completion') : t('app.types.workflow')}</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<ContentDialog
|
||||||
|
show={open}
|
||||||
|
onClose={() => setOpen(false)}
|
||||||
|
className='!p-0 flex flex-col absolute left-2 top-2 bottom-2 w-[420px] rounded-2xl'
|
||||||
|
>
|
||||||
|
<div className='flex p-4 flex-col justify-center items-start gap-3 self-stretch shrink-0'>
|
||||||
|
<div className='flex items-center gap-3 self-stretch'>
|
||||||
|
<AppIcon
|
||||||
|
size="large"
|
||||||
|
iconType={appDetail.icon_type}
|
||||||
|
icon={appDetail.icon}
|
||||||
|
background={appDetail.icon_background}
|
||||||
|
imageUrl={appDetail.icon_url}
|
||||||
|
/>
|
||||||
|
<div className='flex flex-col justify-center items-start grow w-full'>
|
||||||
|
<div className='text-text-secondary system-md-semibold truncate w-full'>{appDetail.name}</div>
|
||||||
|
<div className='text-text-tertiary system-2xs-medium-uppercase'>{appDetail.mode === 'advanced-chat' ? t('app.types.chatbot') : appDetail.mode === 'agent-chat' ? t('app.types.agent') : appDetail.mode === 'chat' ? t('app.types.chatbot') : appDetail.mode === 'completion' ? t('app.types.completion') : t('app.types.workflow')}</div>
|
||||||
</div>
|
</div>
|
||||||
{/* description */}
|
</div>
|
||||||
{appDetail.description && (
|
{/* description */}
|
||||||
<div className='px-4 py-2 text-gray-500 text-xs leading-[18px]'>{appDetail.description}</div>
|
{appDetail.description && (
|
||||||
)}
|
<div className='text-text-tertiary system-xs-regular'>{appDetail.description}</div>
|
||||||
{/* operations */}
|
)}
|
||||||
<Divider className="!my-1" />
|
{/* operations */}
|
||||||
<div className="w-full py-1">
|
<div className='flex items-center gap-1 self-stretch'>
|
||||||
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
|
<Button
|
||||||
|
size={'small'}
|
||||||
|
variant={'secondary'}
|
||||||
|
className='gap-[1px]'
|
||||||
|
onClick={() => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
setShowEditModal(true)
|
setShowEditModal(true)
|
||||||
}}>
|
}}
|
||||||
<span className='text-gray-700 text-sm leading-5'>{t('app.editApp')}</span>
|
>
|
||||||
</div>
|
<RiEditLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||||
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={() => {
|
<span className='text-components-button-secondary-text system-xs-medium'>{t('app.editApp')}</span>
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size={'small'}
|
||||||
|
variant={'secondary'}
|
||||||
|
className='gap-[1px]'
|
||||||
|
onClick={() => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
setShowDuplicateModal(true)
|
setShowDuplicateModal(true)
|
||||||
}}>
|
}}
|
||||||
<span className='text-gray-700 text-sm leading-5'>{t('app.duplicate')}</span>
|
|
||||||
</div>
|
|
||||||
{(appDetail.mode === 'completion' || appDetail.mode === 'chat') && (
|
|
||||||
<>
|
|
||||||
<Divider className="!my-1" />
|
|
||||||
<div
|
|
||||||
className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer'
|
|
||||||
onMouseEnter={() => setShowSwitchTip(appDetail.mode)}
|
|
||||||
onMouseLeave={() => setShowSwitchTip('')}
|
|
||||||
onClick={() => {
|
|
||||||
setOpen(false)
|
|
||||||
setShowSwitchModal(true)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span className='text-gray-700 text-sm leading-5'>{t('app.switch')}</span>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<Divider className="!my-1" />
|
|
||||||
<div className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer' onClick={exportCheck}>
|
|
||||||
<span className='text-gray-700 text-sm leading-5'>{t('app.export')}</span>
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
(appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (
|
|
||||||
<div
|
|
||||||
className='h-9 py-2 px-3 mx-1 flex items-center hover:bg-gray-50 rounded-lg cursor-pointer'
|
|
||||||
onClick={() => {
|
|
||||||
setOpen(false)
|
|
||||||
setShowImportDSLModal(true)
|
|
||||||
}}>
|
|
||||||
<span className='text-gray-700 text-sm leading-5'>{t('workflow.common.importDSL')}</span>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<Divider className="!my-1" />
|
|
||||||
<div className='group h-9 py-2 px-3 mx-1 flex items-center hover:bg-red-50 rounded-lg cursor-pointer' onClick={() => {
|
|
||||||
setOpen(false)
|
|
||||||
setShowConfirmDelete(true)
|
|
||||||
}}>
|
|
||||||
<span className='text-gray-700 text-sm leading-5 group-hover:text-red-500'>
|
|
||||||
{t('common.operation.delete')}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* switch tip */}
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
'hidden absolute left-[324px] top-0 w-[376px] rounded-xl bg-white border-[0.5px] border-[rgba(0,0,0,0.05)] shadow-lg',
|
|
||||||
showSwitchTip && '!block',
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<div className={cn(
|
<RiFileCopy2Line className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||||
'w-full h-[256px] bg-center bg-no-repeat bg-contain rounded-xl',
|
<span className='text-components-button-secondary-text system-xs-medium'>{t('app.duplicate')}</span>
|
||||||
showSwitchTip === 'chat' && s.expertPic,
|
</Button>
|
||||||
showSwitchTip === 'completion' && s.completionPic,
|
<Button
|
||||||
)} />
|
size={'small'}
|
||||||
<div className='px-4 pb-2'>
|
variant={'secondary'}
|
||||||
<div className='flex items-center gap-1 text-gray-700 text-md leading-6 font-semibold'>
|
className='gap-[1px]'
|
||||||
{showSwitchTip === 'chat' ? t('app.types.advanced') : t('app.types.workflow')}
|
onClick={exportCheck}
|
||||||
<span className='px-1 rounded-[5px] bg-white border border-black/8 text-gray-500 text-[10px] leading-[18px] font-medium'>BETA</span>
|
>
|
||||||
</div>
|
<RiFileDownloadLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||||
<div className='text-orange-500 text-xs leading-[18px] font-medium'>{t('app.newApp.advancedFor').toLocaleUpperCase()}</div>
|
<span className='text-components-button-secondary-text system-xs-medium'>{t('app.export')}</span>
|
||||||
<div className='mt-1 text-gray-500 text-sm leading-5'>{t('app.newApp.advancedDescription')}</div>
|
</Button>
|
||||||
</div>
|
{
|
||||||
</div>
|
(appDetail.mode === 'advanced-chat' || appDetail.mode === 'workflow') && (
|
||||||
|
<Button
|
||||||
|
size={'small'}
|
||||||
|
variant={'secondary'}
|
||||||
|
className='gap-[1px]'
|
||||||
|
onClick={() => {
|
||||||
|
setOpen(false)
|
||||||
|
setShowImportDSLModal(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<RiFileUploadLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||||
|
<span className='text-components-button-secondary-text system-xs-medium'>{t('workflow.common.importDSL')}</span>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</PortalToFollowElemContent>
|
</div>
|
||||||
{showSwitchModal && (
|
<div className='flex flex-1'>
|
||||||
<SwitchAppModal
|
<CardView
|
||||||
inAppDetail
|
appId={appDetail.id}
|
||||||
show={showSwitchModal}
|
isInPanel={true}
|
||||||
appDetail={appDetail}
|
className='flex flex-col px-2 py-1 gap-2 grow overflow-auto'
|
||||||
onClose={() => setShowSwitchModal(false)}
|
|
||||||
onSuccess={() => setShowSwitchModal(false)}
|
|
||||||
/>
|
/>
|
||||||
)}
|
</div>
|
||||||
{showEditModal && (
|
<div className='flex p-2 flex-col justify-center items-start gap-3 self-stretch border-t-[0.5px] border-divider-subtle shrink-0 min-h-fit'>
|
||||||
<CreateAppModal
|
<Button
|
||||||
isEditModal
|
size={'medium'}
|
||||||
appName={appDetail.name}
|
variant={'ghost'}
|
||||||
appIconType={appDetail.icon_type}
|
className='gap-0.5'
|
||||||
appIcon={appDetail.icon}
|
onClick={() => {
|
||||||
appIconBackground={appDetail.icon_background}
|
setOpen(false)
|
||||||
appIconUrl={appDetail.icon_url}
|
setShowConfirmDelete(true)
|
||||||
appDescription={appDetail.description}
|
}}
|
||||||
appMode={appDetail.mode}
|
>
|
||||||
appUseIconAsAnswerIcon={appDetail.use_icon_as_answer_icon}
|
<RiDeleteBinLine className='w-4 h-4 text-text-tertiary' />
|
||||||
show={showEditModal}
|
<span className='text-text-tertiary system-sm-medium'>{t('common.operation.deleteApp')}</span>
|
||||||
onConfirm={onEdit}
|
</Button>
|
||||||
onHide={() => setShowEditModal(false)}
|
</div>
|
||||||
/>
|
</ContentDialog>
|
||||||
)}
|
{showSwitchModal && (
|
||||||
{showDuplicateModal && (
|
<SwitchAppModal
|
||||||
<DuplicateAppModal
|
inAppDetail
|
||||||
appName={appDetail.name}
|
show={showSwitchModal}
|
||||||
icon_type={appDetail.icon_type}
|
appDetail={appDetail}
|
||||||
icon={appDetail.icon}
|
onClose={() => setShowSwitchModal(false)}
|
||||||
icon_background={appDetail.icon_background}
|
onSuccess={() => setShowSwitchModal(false)}
|
||||||
icon_url={appDetail.icon_url}
|
/>
|
||||||
show={showDuplicateModal}
|
)}
|
||||||
onConfirm={onCopy}
|
{showEditModal && (
|
||||||
onHide={() => setShowDuplicateModal(false)}
|
<CreateAppModal
|
||||||
/>
|
isEditModal
|
||||||
)}
|
appName={appDetail.name}
|
||||||
{showConfirmDelete && (
|
appIconType={appDetail.icon_type}
|
||||||
<Confirm
|
appIcon={appDetail.icon}
|
||||||
title={t('app.deleteAppConfirmTitle')}
|
appIconBackground={appDetail.icon_background}
|
||||||
content={t('app.deleteAppConfirmContent')}
|
appIconUrl={appDetail.icon_url}
|
||||||
isShow={showConfirmDelete}
|
appDescription={appDetail.description}
|
||||||
onConfirm={onConfirmDelete}
|
appMode={appDetail.mode}
|
||||||
onCancel={() => setShowConfirmDelete(false)}
|
appUseIconAsAnswerIcon={appDetail.use_icon_as_answer_icon}
|
||||||
/>
|
show={showEditModal}
|
||||||
)}
|
onConfirm={onEdit}
|
||||||
{showImportDSLModal && (
|
onHide={() => setShowEditModal(false)}
|
||||||
<UpdateDSLModal
|
/>
|
||||||
onCancel={() => setShowImportDSLModal(false)}
|
)}
|
||||||
onBackup={exportCheck}
|
{showDuplicateModal && (
|
||||||
/>
|
<DuplicateAppModal
|
||||||
)}
|
appName={appDetail.name}
|
||||||
{secretEnvList.length > 0 && (
|
icon_type={appDetail.icon_type}
|
||||||
<DSLExportConfirmModal
|
icon={appDetail.icon}
|
||||||
envList={secretEnvList}
|
icon_background={appDetail.icon_background}
|
||||||
onConfirm={onExport}
|
icon_url={appDetail.icon_url}
|
||||||
onClose={() => setSecretEnvList([])}
|
show={showDuplicateModal}
|
||||||
/>
|
onConfirm={onCopy}
|
||||||
)}
|
onHide={() => setShowDuplicateModal(false)}
|
||||||
</div>
|
/>
|
||||||
</PortalToFollowElem>
|
)}
|
||||||
|
{showConfirmDelete && (
|
||||||
|
<Confirm
|
||||||
|
title={t('app.deleteAppConfirmTitle')}
|
||||||
|
content={t('app.deleteAppConfirmContent')}
|
||||||
|
isShow={showConfirmDelete}
|
||||||
|
onConfirm={onConfirmDelete}
|
||||||
|
onCancel={() => setShowConfirmDelete(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showImportDSLModal && (
|
||||||
|
<UpdateDSLModal
|
||||||
|
onCancel={() => setShowImportDSLModal(false)}
|
||||||
|
onBackup={exportCheck}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{secretEnvList.length > 0 && (
|
||||||
|
<DSLExportConfirmModal
|
||||||
|
envList={secretEnvList}
|
||||||
|
onConfirm={onExport}
|
||||||
|
onClose={() => setSecretEnvList([])}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-start p-1">
|
<div className="flex items-center grow">
|
||||||
{icon && icon_background && iconType === 'app' && (
|
{icon && icon_background && iconType === 'app' && (
|
||||||
<div className='flex-shrink-0 mr-3'>
|
<div className='flex-shrink-0 mr-3'>
|
||||||
<AppIcon icon={icon} background={icon_background} />
|
<AppIcon icon={icon} background={icon_background} />
|
||||||
@ -71,8 +71,10 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
|
|||||||
|
|
||||||
}
|
}
|
||||||
{mode === 'expand' && <div className="group">
|
{mode === 'expand' && <div className="group">
|
||||||
<div className={`flex flex-row items-center text-sm font-semibold text-gray-700 group-hover:text-gray-900 break-all ${textStyle?.main ?? ''}`}>
|
<div className={`flex flex-row items-center system-md-semibold text-text-secondary group-hover:text-text-primary ${textStyle?.main ?? ''}`}>
|
||||||
{name}
|
<div className="max-w-[180px] truncate">
|
||||||
|
{name}
|
||||||
|
</div>
|
||||||
{hoverTip
|
{hoverTip
|
||||||
&& <Tooltip
|
&& <Tooltip
|
||||||
popupContent={
|
popupContent={
|
||||||
@ -86,7 +88,6 @@ export default function AppBasic({ icon, icon_background, name, isExternal, type
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className={`text-xs font-normal text-gray-500 group-hover:text-gray-700 break-all ${textStyle?.extra ?? ''}`}>{type}</div>
|
|
||||||
<div className='text-text-tertiary system-2xs-medium-uppercase'>{isExternal ? t('dataset.externalTag') : ''}</div>
|
<div className='text-text-tertiary system-2xs-medium-uppercase'>{isExternal ? t('dataset.externalTag') : ''}</div>
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,7 +57,7 @@ const AppDetailNav = ({ title, desc, isExternal, icon, icon_background, navigati
|
|||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
shrink-0
|
shrink-0
|
||||||
${expand ? 'p-3' : 'p-2'}
|
${expand ? 'p-2' : 'p-1'}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{iconType === 'app' && (
|
{iconType === 'app' && (
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { HTMLProps } from 'react'
|
|
||||||
import React, { useMemo, useState } from 'react'
|
import React, { useMemo, useState } from 'react'
|
||||||
import {
|
|
||||||
Cog8ToothIcon,
|
|
||||||
DocumentTextIcon,
|
|
||||||
PaintBrushIcon,
|
|
||||||
RocketLaunchIcon,
|
|
||||||
} from '@heroicons/react/24/outline'
|
|
||||||
import { usePathname, useRouter } from 'next/navigation'
|
import { usePathname, useRouter } from 'next/navigation'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import {
|
||||||
|
RiBookOpenLine,
|
||||||
|
RiEqualizer2Line,
|
||||||
|
RiExternalLinkLine,
|
||||||
|
RiPaintBrushLine,
|
||||||
|
RiWindowLine,
|
||||||
|
} from '@remixicon/react'
|
||||||
import SettingsModal from './settings'
|
import SettingsModal from './settings'
|
||||||
import EmbeddedModal from './embedded'
|
import EmbeddedModal from './embedded'
|
||||||
import CustomizeModal from './customize'
|
import CustomizeModal from './customize'
|
||||||
@ -18,7 +18,6 @@ import Tooltip from '@/app/components/base/tooltip'
|
|||||||
import AppBasic from '@/app/components/app-sidebar/basic'
|
import AppBasic from '@/app/components/app-sidebar/basic'
|
||||||
import { asyncRunSafe, randomString } from '@/utils'
|
import { asyncRunSafe, randomString } from '@/utils'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Tag from '@/app/components/base/tag'
|
|
||||||
import Switch from '@/app/components/base/switch'
|
import Switch from '@/app/components/base/switch'
|
||||||
import Divider from '@/app/components/base/divider'
|
import Divider from '@/app/components/base/divider'
|
||||||
import CopyFeedback from '@/app/components/base/copy-feedback'
|
import CopyFeedback from '@/app/components/base/copy-feedback'
|
||||||
@ -28,10 +27,12 @@ import SecretKeyButton from '@/app/components/develop/secret-key/secret-key-butt
|
|||||||
import type { AppDetailResponse } from '@/models/app'
|
import type { AppDetailResponse } from '@/models/app'
|
||||||
import { useAppContext } from '@/context/app-context'
|
import { useAppContext } from '@/context/app-context'
|
||||||
import type { AppSSO } from '@/types/app'
|
import type { AppSSO } from '@/types/app'
|
||||||
|
import Indicator from '@/app/components/header/indicator'
|
||||||
|
|
||||||
export type IAppCardProps = {
|
export type IAppCardProps = {
|
||||||
className?: string
|
className?: string
|
||||||
appInfo: AppDetailResponse & Partial<AppSSO>
|
appInfo: AppDetailResponse & Partial<AppSSO>
|
||||||
|
isInPanel?: boolean
|
||||||
cardType?: 'api' | 'webapp'
|
cardType?: 'api' | 'webapp'
|
||||||
customBgColor?: string
|
customBgColor?: string
|
||||||
onChangeStatus: (val: boolean) => Promise<void>
|
onChangeStatus: (val: boolean) => Promise<void>
|
||||||
@ -39,12 +40,9 @@ export type IAppCardProps = {
|
|||||||
onGenerateCode?: () => Promise<void>
|
onGenerateCode?: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|
||||||
const EmbedIcon = ({ className = '' }: HTMLProps<HTMLDivElement>) => {
|
|
||||||
return <div className={`${style.codeBrowserIcon} ${className}`}></div>
|
|
||||||
}
|
|
||||||
|
|
||||||
function AppCard({
|
function AppCard({
|
||||||
appInfo,
|
appInfo,
|
||||||
|
isInPanel,
|
||||||
cardType = 'webapp',
|
cardType = 'webapp',
|
||||||
customBgColor,
|
customBgColor,
|
||||||
onChangeStatus,
|
onChangeStatus,
|
||||||
@ -66,17 +64,18 @@ function AppCard({
|
|||||||
const OPERATIONS_MAP = useMemo(() => {
|
const OPERATIONS_MAP = useMemo(() => {
|
||||||
const operationsMap = {
|
const operationsMap = {
|
||||||
webapp: [
|
webapp: [
|
||||||
{ opName: t('appOverview.overview.appInfo.preview'), opIcon: RocketLaunchIcon },
|
{ opName: t('appOverview.overview.appInfo.launch'), opIcon: RiExternalLinkLine },
|
||||||
{ opName: t('appOverview.overview.appInfo.customize.entry'), opIcon: PaintBrushIcon },
|
|
||||||
] as { opName: string; opIcon: any }[],
|
] as { opName: string; opIcon: any }[],
|
||||||
api: [{ opName: t('appOverview.overview.apiInfo.doc'), opIcon: DocumentTextIcon }],
|
api: [{ opName: t('appOverview.overview.apiInfo.doc'), opIcon: RiBookOpenLine }],
|
||||||
app: [],
|
app: [],
|
||||||
}
|
}
|
||||||
if (appInfo.mode !== 'completion' && appInfo.mode !== 'workflow')
|
if (appInfo.mode !== 'completion' && appInfo.mode !== 'workflow')
|
||||||
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.embedded.entry'), opIcon: EmbedIcon })
|
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.embedded.entry'), opIcon: RiWindowLine })
|
||||||
|
|
||||||
|
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.customize.entry'), opIcon: RiPaintBrushLine })
|
||||||
|
|
||||||
if (isCurrentWorkspaceEditor)
|
if (isCurrentWorkspaceEditor)
|
||||||
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.settings.entry'), opIcon: Cog8ToothIcon })
|
operationsMap.webapp.push({ opName: t('appOverview.overview.appInfo.settings.entry'), opIcon: RiEqualizer2Line })
|
||||||
|
|
||||||
return operationsMap
|
return operationsMap
|
||||||
}, [isCurrentWorkspaceEditor, appInfo, t])
|
}, [isCurrentWorkspaceEditor, appInfo, t])
|
||||||
@ -92,13 +91,9 @@ function AppCard({
|
|||||||
const appUrl = `${app_base_url}/${appMode}/${access_token}`
|
const appUrl = `${app_base_url}/${appMode}/${access_token}`
|
||||||
const apiUrl = appInfo?.api_base_url
|
const apiUrl = appInfo?.api_base_url
|
||||||
|
|
||||||
let bgColor = 'bg-primary-50 bg-opacity-40'
|
|
||||||
if (cardType === 'api')
|
|
||||||
bgColor = 'bg-purple-50'
|
|
||||||
|
|
||||||
const genClickFuncByName = (opName: string) => {
|
const genClickFuncByName = (opName: string) => {
|
||||||
switch (opName) {
|
switch (opName) {
|
||||||
case t('appOverview.overview.appInfo.preview'):
|
case t('appOverview.overview.appInfo.launch'):
|
||||||
return () => {
|
return () => {
|
||||||
window.open(appUrl, '_blank')
|
window.open(appUrl, '_blank')
|
||||||
}
|
}
|
||||||
@ -135,49 +130,50 @@ function AppCard({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
`shadow-xs border-[0.5px] rounded-lg border-gray-200 ${className ?? ''}`}
|
`${isInPanel ? 'border-l-[0.5px] border-t' : 'shadow-xs border-[0.5px]'} rounded-xl border-effects-highlight w-full max-w-full ${className ?? ''}`}
|
||||||
>
|
>
|
||||||
<div className={`px-6 py-5 ${customBgColor ?? bgColor} rounded-lg`}>
|
<div className={`${customBgColor ?? 'bg-background-default'} rounded-xl`}>
|
||||||
<div className="mb-2.5 flex flex-row items-start justify-between">
|
<div className='flex flex-col p-3 justify-center items-start gap-3 self-stretch border-b-[0.5px] border-divider-subtle w-full'>
|
||||||
<AppBasic
|
<div className='flex items-center gap-3 self-stretch w-full'>
|
||||||
iconType={cardType}
|
<AppBasic
|
||||||
icon={appInfo.icon}
|
iconType={cardType}
|
||||||
icon_background={appInfo.icon_background}
|
icon={appInfo.icon}
|
||||||
name={basicName}
|
icon_background={appInfo.icon_background}
|
||||||
type={
|
name={basicName}
|
||||||
isApp
|
type={
|
||||||
? t('appOverview.overview.appInfo.explanation')
|
isApp
|
||||||
: t('appOverview.overview.apiInfo.explanation')
|
? t('appOverview.overview.appInfo.explanation')
|
||||||
}
|
: t('appOverview.overview.apiInfo.explanation')
|
||||||
/>
|
}
|
||||||
<div className="flex flex-row items-center h-9">
|
/>
|
||||||
<Tag className="mr-2" color={runningStatus ? 'green' : 'yellow'}>
|
<div className='flex items-center gap-1'>
|
||||||
{runningStatus
|
<Indicator color={runningStatus ? 'green' : 'yellow'} />
|
||||||
? t('appOverview.overview.status.running')
|
<div className={`${runningStatus ? 'text-text-success' : 'text-text-warning'} system-xs-semibold-uppercase`}>
|
||||||
: t('appOverview.overview.status.disable')}
|
{runningStatus
|
||||||
</Tag>
|
? t('appOverview.overview.status.running')
|
||||||
|
: t('appOverview.overview.status.disable')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<Switch defaultValue={runningStatus} onChange={onChangeStatus} disabled={toggleDisabled} />
|
<Switch defaultValue={runningStatus} onChange={onChangeStatus} disabled={toggleDisabled} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className='flex flex-col justify-center items-start self-stretch'>
|
||||||
<div className="flex flex-col justify-center py-2">
|
<div className="pb-1 system-xs-medium text-text-tertiary">
|
||||||
<div className="py-1">
|
|
||||||
<div className="pb-1 text-xs text-gray-500">
|
|
||||||
{isApp
|
{isApp
|
||||||
? t('appOverview.overview.appInfo.accessibleAddress')
|
? t('appOverview.overview.appInfo.accessibleAddress')
|
||||||
: t('appOverview.overview.apiInfo.accessibleAddress')}
|
: t('appOverview.overview.apiInfo.accessibleAddress')}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full h-9 pl-2 pr-0.5 py-0.5 bg-black bg-opacity-2 rounded-lg border border-black border-opacity-5 justify-start items-center inline-flex">
|
<div className="w-full h-9 pl-2 p-1 bg-components-input-bg-normal rounded-lg items-center inline-flex gap-0.5">
|
||||||
<div className="h-4 px-2 justify-start items-start gap-2 flex flex-1 min-w-0">
|
<div className="h-4 px-1 justify-start items-start gap-2 flex flex-1 min-w-0">
|
||||||
<div className="text-gray-700 text-xs font-medium text-ellipsis overflow-hidden whitespace-nowrap">
|
<div className="text-text-secondary text-xs font-medium text-ellipsis overflow-hidden whitespace-nowrap">
|
||||||
{isApp ? appUrl : apiUrl}
|
{isApp ? appUrl : apiUrl}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Divider type="vertical" className="!h-3.5 shrink-0 !mx-0.5" />
|
|
||||||
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} selectorId={randomString(8)} className={'hover:bg-gray-200'} />}
|
|
||||||
<CopyFeedback
|
<CopyFeedback
|
||||||
content={isApp ? appUrl : apiUrl}
|
content={isApp ? appUrl : apiUrl}
|
||||||
className={'hover:bg-gray-200'}
|
className={'!size-6'}
|
||||||
/>
|
/>
|
||||||
|
{isApp && <ShareQRCode content={isApp ? appUrl : apiUrl} className='z-50 !size-6 hover:bg-state-base-hover rounded-md' selectorId={randomString(8)} />}
|
||||||
|
{isApp && <Divider type="vertical" className="!h-3.5 shrink-0 !mx-0.5" />}
|
||||||
{/* button copy link/ button regenerate */}
|
{/* button copy link/ button regenerate */}
|
||||||
{showConfirmDelete && (
|
{showConfirmDelete && (
|
||||||
<Confirm
|
<Confirm
|
||||||
@ -197,7 +193,7 @@ function AppCard({
|
|||||||
popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
|
popupContent={t('appOverview.overview.appInfo.regenerate') || ''}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="w-8 h-8 ml-0.5 cursor-pointer hover:bg-gray-200 rounded-lg"
|
className="w-6 h-6 cursor-pointer hover:bg-state-base-hover rounded-md"
|
||||||
onClick={() => setShowConfirmDelete(true)}
|
onClick={() => setShowConfirmDelete(true)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -210,8 +206,8 @@ function AppCard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={'pt-2 flex flex-row items-center flex-wrap gap-y-2'}>
|
<div className={'flex p-3 items-center gap-1 self-stretch'}>
|
||||||
{!isApp && <SecretKeyButton className='flex-shrink-0 !h-8 bg-white mr-2' textCls='!text-gray-700 font-medium' iconCls='stroke-[1.2px]' appId={appInfo.id} />}
|
{!isApp && <SecretKeyButton appId={appInfo.id} />}
|
||||||
{OPERATIONS_MAP[cardType].map((op) => {
|
{OPERATIONS_MAP[cardType].map((op) => {
|
||||||
const disabled
|
const disabled
|
||||||
= op.opName === t('appOverview.overview.appInfo.settings.entry')
|
= op.opName === t('appOverview.overview.appInfo.settings.entry')
|
||||||
@ -219,7 +215,9 @@ function AppCard({
|
|||||||
: !runningStatus
|
: !runningStatus
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className="mr-2"
|
className="mr-1 min-w-[88px]"
|
||||||
|
size="small"
|
||||||
|
variant={'ghost'}
|
||||||
key={op.opName}
|
key={op.opName}
|
||||||
onClick={genClickFuncByName(op.opName)}
|
onClick={genClickFuncByName(op.opName)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@ -230,9 +228,9 @@ function AppCard({
|
|||||||
}
|
}
|
||||||
popupClassName={disabled ? 'mt-[-8px]' : '!hidden'}
|
popupClassName={disabled ? 'mt-[-8px]' : '!hidden'}
|
||||||
>
|
>
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex items-center justify-center gap-[1px]">
|
||||||
<op.opIcon className="h-4 w-4 mr-1.5 stroke-[1.8px]" />
|
<op.opIcon className="h-3.5 w-3.5" />
|
||||||
<span className="text-[13px]">{op.opName}</span>
|
<div className={`${runningStatus ? 'text-text-tertiary' : 'text-components-button-ghost-text-disabled'} system-xs-medium px-[3px]`}>{op.opName}</div>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Button>
|
</Button>
|
||||||
|
59
web/app/components/base/content-dialog/index.tsx
Normal file
59
web/app/components/base/content-dialog/index.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { Fragment, type ReactNode } from 'react'
|
||||||
|
import { Transition } from '@headlessui/react'
|
||||||
|
import classNames from '@/utils/classnames'
|
||||||
|
|
||||||
|
type ContentDialogProps = {
|
||||||
|
className?: string
|
||||||
|
show: boolean
|
||||||
|
onClose?: () => void
|
||||||
|
children: ReactNode
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContentDialog = ({
|
||||||
|
className,
|
||||||
|
show,
|
||||||
|
onClose,
|
||||||
|
children,
|
||||||
|
}: ContentDialogProps) => {
|
||||||
|
return (
|
||||||
|
<Transition
|
||||||
|
show={show}
|
||||||
|
as="div"
|
||||||
|
className="absolute left-0 top-0 w-full h-full z-20 p-2 box-border"
|
||||||
|
>
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="ease-out duration-300"
|
||||||
|
enterFrom="opacity-0"
|
||||||
|
enterTo="opacity-100"
|
||||||
|
leave="ease-in duration-200"
|
||||||
|
leaveFrom="opacity-100"
|
||||||
|
leaveTo="opacity-0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="absolute left-0 inset-0 w-full bg-app-detail-overlay-bg"
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
</Transition.Child>
|
||||||
|
|
||||||
|
<Transition.Child
|
||||||
|
as={Fragment}
|
||||||
|
enter="transform transition ease-out duration-300"
|
||||||
|
enterFrom="-translate-x-full"
|
||||||
|
enterTo="translate-x-0"
|
||||||
|
leave="transform transition ease-in duration-200"
|
||||||
|
leaveFrom="translate-x-0"
|
||||||
|
leaveTo="-translate-x-full"
|
||||||
|
>
|
||||||
|
<div className={classNames(
|
||||||
|
'absolute left-0 w-full bg-app-detail-bg border-r border-divider-burn',
|
||||||
|
className,
|
||||||
|
)}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</Transition.Child>
|
||||||
|
</Transition>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContentDialog
|
@ -35,7 +35,7 @@ const CopyFeedback = ({ content, className }: Props) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg ${
|
className={`w-8 h-8 cursor-pointer hover:bg-state-base-hover rounded-md ${
|
||||||
className ?? ''
|
className ?? ''
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import QRCode from 'qrcode.react'
|
import { QRCodeSVG } from 'qrcode.react'
|
||||||
import QrcodeStyle from './style.module.css'
|
import QrcodeStyle from './style.module.css'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
|
|
||||||
@ -54,20 +54,20 @@ const ShareQRCode = ({ content, selectorId, className }: Props) => {
|
|||||||
popupContent={t(`${prefixEmbedded}`) || ''}
|
popupContent={t(`${prefixEmbedded}`) || ''}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`w-8 h-8 cursor-pointer rounded-lg ${className ?? ''}`}
|
className={`w-8 h-8 cursor-pointer rounded-lg relative ${className ?? ''}`}
|
||||||
onClick={toggleQRCode}
|
onClick={toggleQRCode}
|
||||||
>
|
>
|
||||||
<div className={`w-full h-full ${QrcodeStyle.QrcodeIcon} ${isShow ? QrcodeStyle.show : ''}`} />
|
<div className={`w-full h-full ${QrcodeStyle.QrcodeIcon} ${isShow ? QrcodeStyle.show : ''}`} />
|
||||||
{isShow && (
|
{isShow && (
|
||||||
<div
|
<div
|
||||||
ref={qrCodeRef}
|
ref={qrCodeRef}
|
||||||
className={QrcodeStyle.qrcodeform}
|
className={`${QrcodeStyle.qrcodeform} !absolute right-0 top-0`}
|
||||||
onClick={handlePanelClick}
|
onClick={handlePanelClick}
|
||||||
>
|
>
|
||||||
<QRCode size={160} value={content} className={QrcodeStyle.qrcodeimage}/>
|
<QRCodeSVG size={160} value={content} className={QrcodeStyle.qrcodeimage}/>
|
||||||
<div className={QrcodeStyle.text}>
|
<div className={QrcodeStyle.text}>
|
||||||
<div className={`text-gray-500 ${QrcodeStyle.scan}`}>{t('appOverview.overview.appInfo.qrcode.scan')}</div>
|
<div className={`text-text-tertiary ${QrcodeStyle.scan}`}>{t('appOverview.overview.appInfo.qrcode.scan')}</div>
|
||||||
<div className={`text-gray-500 ${QrcodeStyle.scan}`}>·</div>
|
<div className={`text-text-tertiary ${QrcodeStyle.scan}`}>·</div>
|
||||||
<div className={QrcodeStyle.download} onClick={downloadQR}>{t('appOverview.overview.appInfo.qrcode.download')}</div>
|
<div className={QrcodeStyle.download} onClick={downloadQR}>{t('appOverview.overview.appInfo.qrcode.download')}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,29 +1,31 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { RiKey2Line } from '@remixicon/react'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import SecretKeyModal from '@/app/components/develop/secret-key/secret-key-modal'
|
import SecretKeyModal from '@/app/components/develop/secret-key/secret-key-modal'
|
||||||
// import { KeyIcon } from '@heroicons/react/20/solid'
|
|
||||||
|
|
||||||
type ISecretKeyButtonProps = {
|
type ISecretKeyButtonProps = {
|
||||||
className?: string
|
className?: string
|
||||||
appId?: string
|
appId?: string
|
||||||
iconCls?: string
|
|
||||||
textCls?: string
|
textCls?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const SecretKeyButton = ({ className, appId, iconCls, textCls }: ISecretKeyButtonProps) => {
|
const SecretKeyButton = ({ className, appId, textCls }: ISecretKeyButtonProps) => {
|
||||||
const [isVisible, setVisible] = useState(false)
|
const [isVisible, setVisible] = useState(false)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button className={`px-3 ${className}`} onClick={() => setVisible(true)}>
|
<Button
|
||||||
<div className={'flex items-center justify-center w-4 h-4 mr-2'}>
|
className={`px-3 ${className}`}
|
||||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" className={iconCls}>
|
onClick={() => setVisible(true)}
|
||||||
<path d="M9 3.66672C9.35362 3.66672 9.69276 3.80719 9.94281 4.05724C10.1929 4.30729 10.3333 4.64643 10.3333 5.00005M13 5.00005C13.0002 5.62483 12.854 6.24097 12.5732 6.79908C12.2924 7.3572 11.8847 7.84177 11.3829 8.21397C10.8811 8.58617 10.2991 8.83564 9.68347 8.94239C9.06788 9.04915 8.43584 9.01022 7.838 8.82872L6.33333 10.3334H5V11.6667H3.66667V13.0001H1.66667C1.48986 13.0001 1.32029 12.9298 1.19526 12.8048C1.07024 12.6798 1 12.5102 1 12.3334V10.6094C1.00004 10.4326 1.0703 10.263 1.19533 10.1381L5.17133 6.16205C5.00497 5.61206 4.95904 5.03268 5.0367 4.46335C5.11435 3.89402 5.31375 3.3481 5.62133 2.86275C5.92891 2.3774 6.33744 1.96401 6.81913 1.65073C7.30082 1.33745 7.84434 1.13162 8.41272 1.04725C8.9811 0.96289 9.56098 1.00197 10.1129 1.16184C10.6648 1.32171 11.1758 1.59861 11.6111 1.97369C12.0464 2.34878 12.3958 2.81324 12.6354 3.33548C12.8751 3.85771 12.9994 4.42545 13 5.00005Z" stroke="#1F2A37" strokeLinecap="round" strokeLinejoin="round" />
|
size='small'
|
||||||
</svg>
|
variant='ghost'
|
||||||
|
>
|
||||||
|
<div className={'flex items-center justify-center w-3.5 h-3.5'}>
|
||||||
|
<RiKey2Line className='w-3.5 h-3.5 text-text-tertiary' />
|
||||||
</div>
|
</div>
|
||||||
<div className={`text-[13px] text-gray-800 ${textCls}`}>{t('appApi.apiKey')}</div>
|
<div className={`text-text-tertiary system-xs-medium px-[3px] ${textCls}`}>{t('appApi.apiKey')}</div>
|
||||||
</Button>
|
</Button>
|
||||||
<SecretKeyModal isShow={isVisible} onClose={() => setVisible(false)} appId={appId} />
|
<SecretKeyModal isShow={isVisible} onClose={() => setVisible(false)} appId={appId} />
|
||||||
</>
|
</>
|
||||||
|
@ -33,6 +33,7 @@ const translation = {
|
|||||||
explanation: 'Ready-to-use AI WebApp',
|
explanation: 'Ready-to-use AI WebApp',
|
||||||
accessibleAddress: 'Public URL',
|
accessibleAddress: 'Public URL',
|
||||||
preview: 'Preview',
|
preview: 'Preview',
|
||||||
|
launch: 'Launch',
|
||||||
regenerate: 'Regenerate',
|
regenerate: 'Regenerate',
|
||||||
regenerateNotice: 'Do you want to regenerate the public URL?',
|
regenerateNotice: 'Do you want to regenerate the public URL?',
|
||||||
preUseReminder: 'Please enable WebApp before continuing.',
|
preUseReminder: 'Please enable WebApp before continuing.',
|
||||||
|
@ -27,6 +27,7 @@ const translation = {
|
|||||||
sure: 'I\'m sure',
|
sure: 'I\'m sure',
|
||||||
download: 'Download',
|
download: 'Download',
|
||||||
delete: 'Delete',
|
delete: 'Delete',
|
||||||
|
deleteApp: 'Delete App',
|
||||||
settings: 'Settings',
|
settings: 'Settings',
|
||||||
setup: 'Setup',
|
setup: 'Setup',
|
||||||
getForFree: 'Get for free',
|
getForFree: 'Get for free',
|
||||||
|
@ -33,6 +33,7 @@ const translation = {
|
|||||||
explanation: '开箱即用的 AI WebApp',
|
explanation: '开箱即用的 AI WebApp',
|
||||||
accessibleAddress: '公开访问 URL',
|
accessibleAddress: '公开访问 URL',
|
||||||
preview: '预览',
|
preview: '预览',
|
||||||
|
launch: '启动',
|
||||||
regenerate: '重新生成',
|
regenerate: '重新生成',
|
||||||
regenerateNotice: '您是否要重新生成公开访问 URL?',
|
regenerateNotice: '您是否要重新生成公开访问 URL?',
|
||||||
preUseReminder: '使用前请先打开开关',
|
preUseReminder: '使用前请先打开开关',
|
||||||
|
@ -27,6 +27,7 @@ const translation = {
|
|||||||
sure: '我确定',
|
sure: '我确定',
|
||||||
download: '下载',
|
download: '下载',
|
||||||
delete: '删除',
|
delete: '删除',
|
||||||
|
deleteApp: '删除应用',
|
||||||
settings: '设置',
|
settings: '设置',
|
||||||
setup: '设置',
|
setup: '设置',
|
||||||
getForFree: '免费获取',
|
getForFree: '免费获取',
|
||||||
|
@ -99,6 +99,8 @@ const config = {
|
|||||||
'chatbot-bg': 'var(--color-chatbot-bg)',
|
'chatbot-bg': 'var(--color-chatbot-bg)',
|
||||||
'chat-bubble-bg': 'var(--color-chat-bubble-bg)',
|
'chat-bubble-bg': 'var(--color-chat-bubble-bg)',
|
||||||
'workflow-process-bg': 'var(--color-workflow-process-bg)',
|
'workflow-process-bg': 'var(--color-workflow-process-bg)',
|
||||||
|
'app-detail-bg': 'var(--color-app-detail-bg)',
|
||||||
|
'app-detail-overlay-bg': 'var(--color-app-detail-overlay-bg)',
|
||||||
'dataset-chunk-process-success-bg': 'var(--color-dataset-chunk-process-success-bg)',
|
'dataset-chunk-process-success-bg': 'var(--color-dataset-chunk-process-success-bg)',
|
||||||
'dataset-chunk-process-error-bg': 'var(--color-dataset-chunk-process-error-bg)',
|
'dataset-chunk-process-error-bg': 'var(--color-dataset-chunk-process-error-bg)',
|
||||||
'dataset-chunk-detail-card-hover-bg': 'var(--color-dataset-chunk-detail-card-hover-bg)',
|
'dataset-chunk-detail-card-hover-bg': 'var(--color-dataset-chunk-detail-card-hover-bg)',
|
||||||
|
@ -19,6 +19,17 @@ html[data-theme="dark"] {
|
|||||||
rgba(34, 34, 37, 0.9) -0.1%,
|
rgba(34, 34, 37, 0.9) -0.1%,
|
||||||
rgba(29, 29, 32, 0.9) 98.26%
|
rgba(29, 29, 32, 0.9) 98.26%
|
||||||
);
|
);
|
||||||
|
--color-app-detail-bg: linear-gradient(
|
||||||
|
169deg,
|
||||||
|
#1D1D20 1.18%,
|
||||||
|
#222225 99.52%
|
||||||
|
);
|
||||||
|
--color-app-detail-overlay-bg: linear-gradient(
|
||||||
|
270deg,
|
||||||
|
rgba(0, 0, 0, 0.00) 0%,
|
||||||
|
rgba(24, 24, 27, 0.02) 8%,
|
||||||
|
rgba(24, 24, 27, 0.54) 100%
|
||||||
|
);
|
||||||
--color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
|
--color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
|
||||||
--color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
|
--color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.30) 0%, rgba(0, 0, 0, 0.00) 100%);
|
||||||
--color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #1D1D20 0%, #222225 100%);
|
--color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #1D1D20 0%, #222225 100%);
|
||||||
|
@ -19,6 +19,17 @@ html[data-theme="light"] {
|
|||||||
rgba(249, 250, 251, 0.9) -0.1%,
|
rgba(249, 250, 251, 0.9) -0.1%,
|
||||||
rgba(242, 244, 247, 0.9) 98.26%
|
rgba(242, 244, 247, 0.9) 98.26%
|
||||||
);
|
);
|
||||||
|
--color-app-detail-bg: linear-gradient(
|
||||||
|
169deg,
|
||||||
|
#F2F4F7 1.18%,
|
||||||
|
#F9FAFB 99.52%
|
||||||
|
);
|
||||||
|
--color-app-detail-overlay-bg: linear-gradient(
|
||||||
|
270deg,
|
||||||
|
rgba(0, 0, 0, 0.00) 0%,
|
||||||
|
rgba(16, 24, 40, 0.01) 8%,
|
||||||
|
rgba(16, 24, 40, 0.18) 100%
|
||||||
|
);
|
||||||
--color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
|
--color-dataset-chunk-process-success-bg: linear-gradient(92deg, rgba(23, 178, 106, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
|
||||||
--color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
|
--color-dataset-chunk-process-error-bg: linear-gradient(92deg, rgba(240, 68, 56, 0.25) 0%, rgba(255, 255, 255, 0.00) 100%);
|
||||||
--color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #F2F4F7 0%, #F9FAFB 100%);
|
--color-dataset-chunk-detail-card-hover-bg: linear-gradient(180deg, #F2F4F7 0%, #F9FAFB 100%);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user