Commit 117b8411 authored by JzoNg's avatar JzoNg

app list modification

parent 171dd5c7
......@@ -13,7 +13,9 @@ import type { ConfigParams } from '@/app/components/app/overview/settings'
import type { App } from '@/types/app'
import Confirm from '@/app/components/base/confirm'
import { ToastContext } from '@/app/components/base/toast'
import { deleteApp, fetchAppDetail, updateAppSiteConfig } from '@/service/apps'
import { createApp, deleteApp, fetchAppDetail, updateAppSiteConfig } from '@/service/apps'
import DuplicateAppModal from '@/app/components/app/duplicate-modal'
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
import AppIcon from '@/app/components/base/app-icon'
import AppsContext, { useAppContext } from '@/context/app-context'
import type { HtmlContentProps } from '@/app/components/base/popover'
......@@ -21,6 +23,7 @@ import CustomPopover from '@/app/components/base/popover'
import Divider from '@/app/components/base/divider'
import { asyncRunSafe } from '@/utils'
import { useProviderContext } from '@/context/provider-context'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
export type AppCardProps = {
app: App
......@@ -39,8 +42,9 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
state => state.mutateApps,
)
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
const [showSettingsModal, setShowSettingsModal] = useState(false)
const [showDuplicateModal, setShowDuplicateModal] = useState(false)
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
const [detailState, setDetailState] = useState<{
loading: boolean
detail?: App
......@@ -69,11 +73,10 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
const [err, res] = await asyncRunSafe(
fetchAppDetail({ url: '/apps', id: app.id }),
)
if (!err) {
if (!err)
setDetailState({ loading: false, detail: res })
setShowSettingsModal(true)
}
else { setDetailState({ loading: false }) }
else
setDetailState({ loading: false })
}
const onSaveSiteConfig = useCallback(
......@@ -103,12 +106,49 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
[app.id],
)
const onCreate: DuplicateAppModalProps['onConfirm'] = async ({ name, icon, icon_background }) => {
const { app_model_config: model_config } = await fetchAppDetail({ url: '/apps', id: app.id })
try {
const newApp = await createApp({
name,
icon,
icon_background,
mode: app.mode,
config: model_config,
})
setShowDuplicateModal(false)
notify({
type: 'success',
message: t('app.newApp.appCreated'),
})
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
push(`/app/${newApp.id}/${isCurrentWorkspaceManager ? 'configuration' : 'overview'}`)
}
catch (e) {
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
}
}
const Operations = (props: HtmlContentProps) => {
const onClickSettings = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
props.onClick?.()
e.preventDefault()
await getAppDetail()
setShowSettingsModal(true)
}
const onClickDuplicate = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
props.onClick?.()
e.preventDefault()
setShowDuplicateModal(true)
}
const onClickExport = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
props.onClick?.()
e.preventDefault()
// TODO export
}
const onClickDelete = async (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
......@@ -121,7 +161,13 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
<button className={s.actionItem} onClick={onClickSettings} disabled={detailState.loading}>
<span className={s.actionName}>{t('common.operation.settings')}</span>
</button>
<Divider className="!my-1" />
<button className={s.actionItem} onClick={onClickDuplicate} disabled={detailState.loading}>
<span className={s.actionName}>{t('app.duplicate')}</span>
</button>
<button className={s.actionItem} onClick={onClickExport} disabled={detailState.loading}>
<span className={s.actionName}>{t('app.export')}</span>
</button>
<Divider className="!my-1" />
<div
className={cn(s.actionItem, s.deleteActionItem, 'group')}
......@@ -149,7 +195,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
>
<div className={style.listItemTitle}>
<AppIcon
size="small"
size="large"
icon={app.icon}
background={app.icon_background}
/>
......@@ -175,9 +221,26 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
{app.model_config?.pre_prompt}
</div>
<div className={style.listItemFooter}>
<AppModeLabel mode={app.mode} isAgent={app.is_agent} />
<AppModeLabel mode={app.mode} />
</div>
{showSettingsModal && detailState.detail && (
<SettingsModal
appInfo={detailState.detail}
isShow={showSettingsModal}
onClose={() => setShowSettingsModal(false)}
onSave={onSaveSiteConfig}
/>
)}
{showDuplicateModal && (
<DuplicateAppModal
appName={app.name}
icon={app.icon}
icon_background={app.icon_background}
show={showDuplicateModal}
onConfirm={onCreate}
onHide={() => setShowDuplicateModal(false)}
/>
)}
{showConfirmDelete && (
<Confirm
title={t('app.deleteAppConfirmTitle')}
......@@ -188,14 +251,6 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
onCancel={() => setShowConfirmDelete(false)}
/>
)}
{showSettingsModal && detailState.detail && (
<SettingsModal
appInfo={detailState.detail}
isShow={showSettingsModal}
onClose={() => setShowSettingsModal(false)}
onSave={onSaveSiteConfig}
/>
)}
</div>
</>
)
......
......@@ -2,52 +2,40 @@
import { useTranslation } from 'react-i18next'
import { type AppMode } from '@/types/app'
import {
AiText,
CuteRobote,
} from '@/app/components/base/icons/src/vender/solid/communication'
import { CuteRobote } from '@/app/components/base/icons/src/vender/solid/communication'
import { BubbleText } from '@/app/components/base/icons/src/vender/solid/education'
import { Route } from '@/app/components/base/icons/src/vender/line/mapsAndTravel'
export type AppModeLabelProps = {
mode: AppMode
isAgent?: boolean
className?: string
}
const AppModeLabel = ({
mode,
isAgent,
className,
}: AppModeLabelProps) => {
const { t } = useTranslation()
return (
<div className={`inline-flex items-center px-2 h-6 rounded-md border border-gray-100 text-xs text-gray-500 ${className}`}>
{
mode === 'completion' && (
<>
<AiText className='mr-1 w-3 h-3 text-gray-400' />
{t('app.newApp.completeApp')}
</>
)
}
{
mode === 'chat' && !isAgent && (
<>
<BubbleText className='mr-1 w-3 h-3 text-gray-400' />
{t('appDebug.assistantType.chatAssistant.name')}
</>
)
}
{
mode === 'chat' && isAgent && (
<>
<CuteRobote className='mr-1 w-3 h-3 text-gray-400' />
{t('appDebug.assistantType.agentAssistant.name')}
</>
)
}
</div>
<>
{mode === 'chat' && (
<div className='inline-flex items-center px-2 h-6 rounded-md bg-indigo-25 border border-indigo-100 text-xs text-indigo-600'>
<BubbleText className='mr-1 w-3 h-3' />
{t('app.types.chatbot')}
</div>
)}
{mode === 'agent' && (
<div className='inline-flex items-center px-2 h-6 rounded-md bg-indigo-25 border border-indigo-100 text-xs text-indigo-600'>
<CuteRobote className='mr-1 w-3 h-3' />
{t('app.types.agent')}
</div>
)}
{mode === 'workflow' && (
<div className='inline-flex items-center px-2 h-6 rounded-md bg-[#fffcf5] border border-[#fef0c7] text-xs text-[#f79009]'>
<Route className='mr-1 w-3 h-3' />
{t('app.types.workflow')}
</div>
)}
</>
)
}
......
......@@ -11,7 +11,7 @@ import { fetchAppList } from '@/service/apps'
import { useAppContext } from '@/context/app-context'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { CheckModal } from '@/hooks/use-pay'
import TabSlider from '@/app/components/base/tab-slider'
import TabSliderNew from '@/app/components/base/tab-slider-new'
import { SearchLg } from '@/app/components/base/icons/src/vender/line/general'
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
......@@ -48,8 +48,9 @@ const Apps = () => {
const anchorRef = useRef<HTMLDivElement>(null)
const options = [
{ value: 'all', text: t('app.types.all') },
{ value: 'chat', text: t('app.types.assistant') },
{ value: 'completion', text: t('app.types.completion') },
{ value: 'chat', text: t('app.types.chatbot') },
{ value: 'agent', text: t('app.types.agent') },
{ value: 'workflow', text: t('app.types.workflow') },
]
useEffect(() => {
......@@ -88,6 +89,11 @@ const Apps = () => {
return (
<>
<div className='sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-10 flex-wrap gap-y-2'>
<TabSliderNew
value={activeTab}
onChange={setActiveTab}
options={options}
/>
<div className="flex items-center px-2 w-[200px] h-8 rounded-lg bg-gray-200">
<div className="pointer-events-none shrink-0 flex items-center mr-1.5 justify-center w-4 h-4">
<SearchLg className="h-3.5 w-3.5 text-gray-500" aria-hidden="true" />
......@@ -114,12 +120,6 @@ const Apps = () => {
)
}
</div>
<TabSlider
value={activeTab}
onChange={setActiveTab}
options={options}
/>
</div>
<nav className='grid content-start grid-cols-1 gap-4 px-12 pt-2 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 grow shrink-0'>
{isCurrentWorkspaceManager
......
'use client'
import { forwardRef, useState } from 'react'
import classNames from 'classnames'
import { useTranslation } from 'react-i18next'
import style from '../list.module.css'
import NewAppDialog from './NewAppDialog'
import { useProviderContext } from '@/context/provider-context'
import { Plus } from '@/app/components/base/icons/src/vender/line/general'
import { ArrowUpRight } from '@/app/components/base/icons/src/vender/line/arrows'
export type CreateAppCardProps = {
onSuccess?: () => void
}
// eslint-disable-next-line react/display-name
const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuccess }, ref) => {
const { t } = useTranslation()
const { onPlanInfoChanged } = useProviderContext()
const [showNewAppDialog, setShowNewAppDialog] = useState(false)
return (
<a ref={ref} className={classNames(style.listItem, style.newItemCard)} onClick={() => setShowNewAppDialog(true)}>
<div className={style.listItemTitle}>
<span className={style.newItemIcon}>
<span className={classNames(style.newItemIconImage, style.newItemIconAdd)} />
</span>
<div className={classNames(style.listItemHeading, style.newItemCardHeading)}>
{t('app.createApp')}
<a
ref={ref}
onClick={() => setShowNewAppDialog(true)}
className='relative col-span-1 flex flex-col justify-between min-h-[160px] bg-gray-200 rounded-xl cursor-pointer duration-200 ease-in-out hover:bg-gray-50 hover:shadow-lg transition-colors'
>
<div className='grow rounded-t-xl group hover:bg-white'>
<div className='flex pt-4 px-4 pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'>
<span className='w-10 h-10 p-3 bg-gray-100 rounded-lg border border-gray-200 group-hover:bg-primary-50 group-hover:border-primary-100'>
<Plus className='w-4 h-4 text-gray-500 group-hover:text-primary-600'/>
</span>
<div className='relative grow h-8 text-sm font-medium leading-8 transition-colors duration-200 ease-in-out group-hover:text-primary-600'>
{t('app.createApp')}
</div>
</div>
</div>
{/* <div className='text-xs text-gray-500'>{t('app.createFromConfigFile')}</div> */}
<div className='flex items-center px-4 py-3 border-t-[0.5px] border-black/[.05] rounded-b-xl text-xs leading-[18px] text-gray-500 hover:bg-white hover:text-primary-600'>
{t('app.createFromConfigFile')}
<ArrowUpRight className='ml-1 w-3 h-3'/>
</div>
<NewAppDialog show={showNewAppDialog} onSuccess={
() => {
onPlanInfoChanged()
......
......@@ -10,7 +10,7 @@
mask-image: url(~@/assets/action.svg);
}
.actionItem {
@apply h-9 py-2 px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer;
@apply h-8 py-[6px] px-3 mx-1 flex items-center gap-2 hover:bg-gray-100 rounded-lg cursor-pointer;
width: calc(100% - 0.5rem);
}
.deleteActionItem {
......
......@@ -10,7 +10,7 @@ import Datasets from './Datasets'
import DatasetFooter from './DatasetFooter'
import ApiServer from './ApiServer'
import Doc from './Doc'
import TabSlider from '@/app/components/base/tab-slider'
import TabSliderNew from '@/app/components/base/tab-slider-new'
// Services
import { fetchDatasetApiBaseUrl } from '@/service/datasets'
......@@ -30,7 +30,7 @@ const Container = () => {
return (
<div ref={containerRef} className='grow relative flex flex-col bg-gray-100 overflow-y-auto'>
<div className='sticky top-0 flex justify-between pt-4 px-12 pb-2 leading-[56px] bg-gray-100 z-10 flex-wrap gap-y-2'>
<TabSlider
<TabSliderNew
value={activeTab}
onChange={newActiveTab => setActiveTab(newActiveTab)}
options={options}
......
.listItem {
@apply col-span-1 bg-white border-2 border-solid border-transparent rounded-lg shadow-xs min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg;
@apply col-span-1 bg-white border-2 border-solid border-transparent rounded-xl shadow-xs min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg;
}
.listItem.newItemCard {
......
'use client'
import React, { useState } from 'react'
import cn from 'classnames'
import { useTranslation } from 'react-i18next'
import s from './style.module.css'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import Toast from '@/app/components/base/toast'
import AppIcon from '@/app/components/base/app-icon'
import EmojiPicker from '@/app/components/base/emoji-picker'
import { useProviderContext } from '@/context/provider-context'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
export type DuplicateAppModalProps = {
appName: string
icon: string
icon_background: string
show: boolean
onConfirm: (info: {
name: string
icon: string
icon_background: string
}) => Promise<void>
onHide: () => void
}
const DuplicateAppModal = ({
appName,
icon,
icon_background,
show = false,
onConfirm,
onHide,
}: DuplicateAppModalProps) => {
const { t } = useTranslation()
const [name, setName] = React.useState(appName)
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
const [emoji, setEmoji] = useState({ icon, icon_background })
const { plan, enableBilling } = useProviderContext()
const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps)
const submit = () => {
if (!name.trim()) {
Toast.notify({ type: 'error', message: t('explore.appCustomize.nameRequired') })
return
}
onConfirm({
name,
...emoji,
})
onHide()
}
return (
<>
<Modal
isShow={show}
onClose={() => { }}
className={cn(s.modal, '!max-w-[480px]', 'px-8')}
>
<span className={s.close} onClick={onHide} />
<div className={s.title}>{t('app.duplicateTitle')}</div>
<div className={s.content}>
<div className={s.subTitle}>{t('explore.appCustomize.subTitle')}</div>
<div className='flex items-center justify-between space-x-3'>
<AppIcon size='large' onClick={() => { setShowEmojiPicker(true) }} className='cursor-pointer' icon={emoji.icon} background={emoji.icon_background} />
<input
value={name}
onChange={e => setName(e.target.value)}
className='h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow'
/>
</div>
{/* TODO loc */}
{isAppsFull && <AppsFull loc='app-duplicate-create' />}
</div>
<div className='flex flex-row-reverse'>
<Button disabled={isAppsFull} className='w-24 ml-2' type='primary' onClick={submit}>{t('app.duplicate')}</Button>
<Button className='w-24' onClick={onHide}>{t('common.operation.cancel')}</Button>
</div>
</Modal>
{showEmojiPicker && <EmojiPicker
onSelect={(icon, icon_background) => {
setEmoji({ icon, icon_background })
setShowEmojiPicker(false)
}}
onClose={() => {
setEmoji({ icon, icon_background })
setShowEmojiPicker(false)
}}
/>}
</>
)
}
export default DuplicateAppModal
.modal {
position: relative;
}
.modal .close {
position: absolute;
right: 16px;
top: 25px;
width: 32px;
height: 32px;
border-radius: 8px;
background: center no-repeat url(~@/app/components/datasets/create/assets/close.svg);
background-size: 16px;
cursor: pointer;
}
.modal .title {
@apply mb-9;
font-weight: 600;
font-size: 20px;
line-height: 30px;
color: #101828;
}
.modal .content {
@apply mb-9;
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: #101828;
}
.subTitle {
margin-bottom: 8px;
font-weight: 500;
}
\ No newline at end of file
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="route" clip-path="url(#clip0_664_8452)">
<path id="Icon" d="M5.75 2.5H5.9672C7.49082 2.5 8.25263 2.5 8.54182 2.77364C8.79179 3.01018 8.90257 3.35864 8.83508 3.69611C8.75701 4.08651 8.13505 4.52643 6.89114 5.40627L4.85886 6.84373C3.61495 7.72357 2.99299 8.16349 2.91492 8.5539C2.84743 8.89136 2.95821 9.23982 3.20818 9.47636C3.49737 9.75 4.25918 9.75 5.7828 9.75H6.25M4 2.5C4 3.32843 3.32843 4 2.5 4C1.67157 4 1 3.32843 1 2.5C1 1.67157 1.67157 1 2.5 1C3.32843 1 4 1.67157 4 2.5ZM11 9.5C11 10.3284 10.3284 11 9.5 11C8.67157 11 8 10.3284 8 9.5C8 8.67157 8.67157 8 9.5 8C10.3284 8 11 8.67157 11 9.5Z" stroke="#F79009" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_664_8452">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"id": "route",
"clip-path": "url(#clip0_664_8452)"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"id": "Icon",
"d": "M5.75 2.5H5.9672C7.49082 2.5 8.25263 2.5 8.54182 2.77364C8.79179 3.01018 8.90257 3.35864 8.83508 3.69611C8.75701 4.08651 8.13505 4.52643 6.89114 5.40627L4.85886 6.84373C3.61495 7.72357 2.99299 8.16349 2.91492 8.5539C2.84743 8.89136 2.95821 9.23982 3.20818 9.47636C3.49737 9.75 4.25918 9.75 5.7828 9.75H6.25M4 2.5C4 3.32843 3.32843 4 2.5 4C1.67157 4 1 3.32843 1 2.5C1 1.67157 1.67157 1 2.5 1C3.32843 1 4 1.67157 4 2.5ZM11 9.5C11 10.3284 10.3284 11 9.5 11C8.67157 11 8 10.3284 8 9.5C8 8.67157 8.67157 8 9.5 8C10.3284 8 11 8.67157 11 9.5Z",
"stroke": "currentColor",
"stroke-width": "1.25",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
},
{
"type": "element",
"name": "defs",
"attributes": {},
"children": [
{
"type": "element",
"name": "clipPath",
"attributes": {
"id": "clip0_664_8452"
},
"children": [
{
"type": "element",
"name": "rect",
"attributes": {
"width": "12",
"height": "12",
"fill": "white"
},
"children": []
}
]
}
]
}
]
},
"name": "Route"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './Route.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 = 'Route'
export default Icon
export { default as Globe01 } from './Globe01'
export { default as Route } from './Route'
import type { FC } from 'react'
import cn from 'classnames'
type Option = {
value: string
text: string
}
type TabSliderProps = {
className?: string
value: string
onChange: (v: string) => void
options: Option[]
}
const TabSliderNew: FC<TabSliderProps> = ({
className,
value,
onChange,
options,
}) => {
return (
<div className={cn(className, 'relative flex')}>
{options.map(option => (
<div
key={option.value}
onClick={() => onChange(option.value)}
className={cn(
'mr-1 px-3 py-[5px] h-[28px] rounded-lg border-[0.5px] border-transparent text-gray-700 text-[13px] font-medium leading-[18px] cursor-pointer hover:bg-gray-200',
value === option.value && 'bg-white border-gray-200 shadow-xs text-primary-600 hover:bg-white',
)}
>
{option.text}
</div>
))}
</div>
)
}
export default TabSliderNew
const translation = {
createApp: 'Create new App',
createApp: 'Create App',
types: {
all: 'All',
assistant: 'Assistant',
completion: 'Completion',
chatbot: 'Chatbot',
agent: 'Agent',
workflow: 'Workflow',
},
modes: {
completion: 'Text Generator',
chat: 'Basic Assistant',
},
createFromConfigFile: 'Create app from config file',
duplicate: 'Duplicate',
duplicateTitle: 'Duplicate App',
export: 'Export DSL',
createFromConfigFile: 'Create from DSL file',
deleteAppConfirmTitle: 'Delete this app?',
deleteAppConfirmContent:
'Deleting the app is irreversible. Users will no longer be able to access your app, and all prompt configurations and logs will be permanently deleted.',
......
const translation = {
createApp: 'Criar novo aplicativo',
createApp: 'Criar aplicativo',
types: {
all: 'Tudo',
chatbot: 'Chatbot',
agent: 'Agente',
workflow: 'Fluxo de trabalho',
},
modes: {
completion: 'Gerador de Texto',
chat: 'Aplicativo de Chat',
},
createFromConfigFile: 'Criar aplicativo a partir do arquivo de configuração',
duplicate: 'Duplicar',
duplicateTitle: 'Duplicate aplicativo',
export: 'Exportar DSL',
createFromConfigFile: 'Criar através do arquivo DSL',
deleteAppConfirmTitle: 'Excluir este aplicativo?',
deleteAppConfirmContent:
'A exclusão do aplicativo é irreversível. Os usuários não poderão mais acessar seu aplicativo e todas as configurações de prompt e logs serão excluídas permanentemente.',
......
const translation = { // Add the Ukrainian translation object
createApp: 'Створити новий додаток',
createApp: 'Створити додаток',
types: {
all: 'Всі',
assistant: 'Асистент',
completion: 'Автодоповнення',
all: 'Все',
chatbot: 'Чатбот',
agent: 'Агент',
workflow: 'Робочий Процес',
},
modes: {
completion: 'Генератор тексту',
chat: 'Базовий асистент',
},
createFromConfigFile: 'Створити додаток з файла конфігурації',
duplicate: 'Дублювати',
duplicateTitle: 'Дублювати додаток',
export: 'Експортувати DSL',
createFromConfigFile: 'Створити через файл DSL',
deleteAppConfirmTitle: 'Видалити цей додаток?',
deleteAppConfirmContent:
'Видалення додатка є незворотнім. Користувачі більше не матимуть доступ до вашого додатка, всі конфігурації підказок та журнали будуть видалені назавжди.',
......
......@@ -2,14 +2,18 @@ const translation = {
createApp: '创建应用',
types: {
all: '全部',
assistant: '助手',
completion: '文本生成',
chatbot: '聊天助手',
agent: 'Agent',
workflow: '工作流',
},
modes: {
completion: '文本生成型',
chat: '基础助手',
},
createFromConfigFile: '通过导入应用配置文件创建',
duplicate: '复制',
duplicateTitle: '复制应用',
export: '导出 DSL',
createFromConfigFile: '通过 DSL 文件创建',
deleteAppConfirmTitle: '确认删除应用?',
deleteAppConfirmContent:
'删除应用将无法撤销。用户将不能访问你的应用,所有 Prompt 编排配置和日志均将一并被删除。',
......
import type { App, AppTemplate, SiteConfig } from '@/types/app'
export type AppMode = 'chat' | 'completion'
/* export type App = {
id: string
name: string
......
import type { AppMode } from './app'
import type { AppMode } from '@/types/app'
export type AppBasicInfo = {
id: string
name: string
......
......@@ -44,7 +44,7 @@ export type VariableInput = {
/**
* App modes
*/
export const AppModes = ['completion', 'chat'] as const
export const AppModes = ['agent', 'chat', 'workflow'] as const
export type AppMode = typeof AppModes[number]
/**
......@@ -286,7 +286,6 @@ export type App = {
/** Mode */
mode: AppMode
is_agent: boolean
/** Enable web app */
enable_site: boolean
/** Enable web API */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment