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' ...@@ -13,7 +13,9 @@ import type { ConfigParams } from '@/app/components/app/overview/settings'
import type { App } from '@/types/app' import type { App } from '@/types/app'
import Confirm from '@/app/components/base/confirm' import Confirm from '@/app/components/base/confirm'
import { ToastContext } from '@/app/components/base/toast' 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 AppIcon from '@/app/components/base/app-icon'
import AppsContext, { useAppContext } from '@/context/app-context' import AppsContext, { useAppContext } from '@/context/app-context'
import type { HtmlContentProps } from '@/app/components/base/popover' import type { HtmlContentProps } from '@/app/components/base/popover'
...@@ -21,6 +23,7 @@ import CustomPopover from '@/app/components/base/popover' ...@@ -21,6 +23,7 @@ import CustomPopover from '@/app/components/base/popover'
import Divider from '@/app/components/base/divider' import Divider from '@/app/components/base/divider'
import { asyncRunSafe } from '@/utils' import { asyncRunSafe } from '@/utils'
import { useProviderContext } from '@/context/provider-context' import { useProviderContext } from '@/context/provider-context'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
export type AppCardProps = { export type AppCardProps = {
app: App app: App
...@@ -39,8 +42,9 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ...@@ -39,8 +42,9 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
state => state.mutateApps, state => state.mutateApps,
) )
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
const [showSettingsModal, setShowSettingsModal] = useState(false) const [showSettingsModal, setShowSettingsModal] = useState(false)
const [showDuplicateModal, setShowDuplicateModal] = useState(false)
const [showConfirmDelete, setShowConfirmDelete] = useState(false)
const [detailState, setDetailState] = useState<{ const [detailState, setDetailState] = useState<{
loading: boolean loading: boolean
detail?: App detail?: App
...@@ -69,11 +73,10 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ...@@ -69,11 +73,10 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
const [err, res] = await asyncRunSafe( const [err, res] = await asyncRunSafe(
fetchAppDetail({ url: '/apps', id: app.id }), fetchAppDetail({ url: '/apps', id: app.id }),
) )
if (!err) { if (!err)
setDetailState({ loading: false, detail: res }) setDetailState({ loading: false, detail: res })
setShowSettingsModal(true) else
} setDetailState({ loading: false })
else { setDetailState({ loading: false }) }
} }
const onSaveSiteConfig = useCallback( const onSaveSiteConfig = useCallback(
...@@ -103,12 +106,49 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ...@@ -103,12 +106,49 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
[app.id], [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 Operations = (props: HtmlContentProps) => {
const onClickSettings = async (e: React.MouseEvent<HTMLButtonElement>) => { const onClickSettings = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation() e.stopPropagation()
props.onClick?.() props.onClick?.()
e.preventDefault() e.preventDefault()
await getAppDetail() 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>) => { const onClickDelete = async (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation() e.stopPropagation()
...@@ -121,7 +161,13 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ...@@ -121,7 +161,13 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
<button className={s.actionItem} onClick={onClickSettings} disabled={detailState.loading}> <button className={s.actionItem} onClick={onClickSettings} disabled={detailState.loading}>
<span className={s.actionName}>{t('common.operation.settings')}</span> <span className={s.actionName}>{t('common.operation.settings')}</span>
</button> </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" /> <Divider className="!my-1" />
<div <div
className={cn(s.actionItem, s.deleteActionItem, 'group')} className={cn(s.actionItem, s.deleteActionItem, 'group')}
...@@ -149,7 +195,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ...@@ -149,7 +195,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
> >
<div className={style.listItemTitle}> <div className={style.listItemTitle}>
<AppIcon <AppIcon
size="small" size="large"
icon={app.icon} icon={app.icon}
background={app.icon_background} background={app.icon_background}
/> />
...@@ -175,9 +221,26 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ...@@ -175,9 +221,26 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
{app.model_config?.pre_prompt} {app.model_config?.pre_prompt}
</div> </div>
<div className={style.listItemFooter}> <div className={style.listItemFooter}>
<AppModeLabel mode={app.mode} isAgent={app.is_agent} /> <AppModeLabel mode={app.mode} />
</div> </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 && ( {showConfirmDelete && (
<Confirm <Confirm
title={t('app.deleteAppConfirmTitle')} title={t('app.deleteAppConfirmTitle')}
...@@ -188,14 +251,6 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ...@@ -188,14 +251,6 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
onCancel={() => setShowConfirmDelete(false)} onCancel={() => setShowConfirmDelete(false)}
/> />
)} )}
{showSettingsModal && detailState.detail && (
<SettingsModal
appInfo={detailState.detail}
isShow={showSettingsModal}
onClose={() => setShowSettingsModal(false)}
onSave={onSaveSiteConfig}
/>
)}
</div> </div>
</> </>
) )
......
...@@ -2,52 +2,40 @@ ...@@ -2,52 +2,40 @@
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { type AppMode } from '@/types/app' import { type AppMode } from '@/types/app'
import { import { CuteRobote } from '@/app/components/base/icons/src/vender/solid/communication'
AiText,
CuteRobote,
} from '@/app/components/base/icons/src/vender/solid/communication'
import { BubbleText } from '@/app/components/base/icons/src/vender/solid/education' 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 = { export type AppModeLabelProps = {
mode: AppMode mode: AppMode
isAgent?: boolean
className?: string
} }
const AppModeLabel = ({ const AppModeLabel = ({
mode, mode,
isAgent,
className,
}: AppModeLabelProps) => { }: AppModeLabelProps) => {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
<div className={`inline-flex items-center px-2 h-6 rounded-md border border-gray-100 text-xs text-gray-500 ${className}`}> <>
{ {mode === 'chat' && (
mode === 'completion' && ( <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' />
<AiText className='mr-1 w-3 h-3 text-gray-400' /> {t('app.types.chatbot')}
{t('app.newApp.completeApp')} </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' />
mode === 'chat' && !isAgent && ( {t('app.types.agent')}
<> </div>
<BubbleText className='mr-1 w-3 h-3 text-gray-400' /> )}
{t('appDebug.assistantType.chatAssistant.name')} {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>
mode === 'chat' && isAgent && ( )}
<> </>
<CuteRobote className='mr-1 w-3 h-3 text-gray-400' />
{t('appDebug.assistantType.agentAssistant.name')}
</>
)
}
</div>
) )
} }
......
...@@ -11,7 +11,7 @@ import { fetchAppList } from '@/service/apps' ...@@ -11,7 +11,7 @@ import { fetchAppList } from '@/service/apps'
import { useAppContext } from '@/context/app-context' import { useAppContext } from '@/context/app-context'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { CheckModal } from '@/hooks/use-pay' 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 { SearchLg } from '@/app/components/base/icons/src/vender/line/general'
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general' import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
...@@ -48,8 +48,9 @@ const Apps = () => { ...@@ -48,8 +48,9 @@ const Apps = () => {
const anchorRef = useRef<HTMLDivElement>(null) const anchorRef = useRef<HTMLDivElement>(null)
const options = [ const options = [
{ value: 'all', text: t('app.types.all') }, { value: 'all', text: t('app.types.all') },
{ value: 'chat', text: t('app.types.assistant') }, { value: 'chat', text: t('app.types.chatbot') },
{ value: 'completion', text: t('app.types.completion') }, { value: 'agent', text: t('app.types.agent') },
{ value: 'workflow', text: t('app.types.workflow') },
] ]
useEffect(() => { useEffect(() => {
...@@ -88,6 +89,11 @@ const Apps = () => { ...@@ -88,6 +89,11 @@ const Apps = () => {
return ( 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'> <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="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"> <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" /> <SearchLg className="h-3.5 w-3.5 text-gray-500" aria-hidden="true" />
...@@ -114,12 +120,6 @@ const Apps = () => { ...@@ -114,12 +120,6 @@ const Apps = () => {
) )
} }
</div> </div>
<TabSlider
value={activeTab}
onChange={setActiveTab}
options={options}
/>
</div> </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'> <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 {isCurrentWorkspaceManager
......
'use client' 'use client'
import { forwardRef, useState } from 'react' import { forwardRef, useState } from 'react'
import classNames from 'classnames'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import style from '../list.module.css'
import NewAppDialog from './NewAppDialog' import NewAppDialog from './NewAppDialog'
import { useProviderContext } from '@/context/provider-context' 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 = { export type CreateAppCardProps = {
onSuccess?: () => void onSuccess?: () => void
} }
// eslint-disable-next-line react/display-name
const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuccess }, ref) => { const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuccess }, ref) => {
const { t } = useTranslation() const { t } = useTranslation()
const { onPlanInfoChanged } = useProviderContext() const { onPlanInfoChanged } = useProviderContext()
const [showNewAppDialog, setShowNewAppDialog] = useState(false) const [showNewAppDialog, setShowNewAppDialog] = useState(false)
return ( return (
<a ref={ref} className={classNames(style.listItem, style.newItemCard)} onClick={() => setShowNewAppDialog(true)}> <a
<div className={style.listItemTitle}> ref={ref}
<span className={style.newItemIcon}> onClick={() => setShowNewAppDialog(true)}
<span className={classNames(style.newItemIconImage, style.newItemIconAdd)} /> 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'
</span> >
<div className={classNames(style.listItemHeading, style.newItemCardHeading)}> <div className='grow rounded-t-xl group hover:bg-white'>
{t('app.createApp')} <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> </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={ <NewAppDialog show={showNewAppDialog} onSuccess={
() => { () => {
onPlanInfoChanged() onPlanInfoChanged()
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
mask-image: url(~@/assets/action.svg); mask-image: url(~@/assets/action.svg);
} }
.actionItem { .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); width: calc(100% - 0.5rem);
} }
.deleteActionItem { .deleteActionItem {
......
...@@ -10,7 +10,7 @@ import Datasets from './Datasets' ...@@ -10,7 +10,7 @@ import Datasets from './Datasets'
import DatasetFooter from './DatasetFooter' import DatasetFooter from './DatasetFooter'
import ApiServer from './ApiServer' import ApiServer from './ApiServer'
import Doc from './Doc' import Doc from './Doc'
import TabSlider from '@/app/components/base/tab-slider' import TabSliderNew from '@/app/components/base/tab-slider-new'
// Services // Services
import { fetchDatasetApiBaseUrl } from '@/service/datasets' import { fetchDatasetApiBaseUrl } from '@/service/datasets'
...@@ -30,7 +30,7 @@ const Container = () => { ...@@ -30,7 +30,7 @@ const Container = () => {
return ( return (
<div ref={containerRef} className='grow relative flex flex-col bg-gray-100 overflow-y-auto'> <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'> <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} value={activeTab}
onChange={newActiveTab => setActiveTab(newActiveTab)} onChange={newActiveTab => setActiveTab(newActiveTab)}
options={options} options={options}
......
.listItem { .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 { .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 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 = { const translation = {
createApp: 'Create new App', createApp: 'Create App',
types: { types: {
all: 'All', all: 'All',
assistant: 'Assistant', chatbot: 'Chatbot',
completion: 'Completion', agent: 'Agent',
workflow: 'Workflow',
}, },
modes: { modes: {
completion: 'Text Generator', completion: 'Text Generator',
chat: 'Basic Assistant', 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?', deleteAppConfirmTitle: 'Delete this app?',
deleteAppConfirmContent: 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.', '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 = { const translation = {
createApp: 'Criar novo aplicativo', createApp: 'Criar aplicativo',
types: {
all: 'Tudo',
chatbot: 'Chatbot',
agent: 'Agente',
workflow: 'Fluxo de trabalho',
},
modes: { modes: {
completion: 'Gerador de Texto', completion: 'Gerador de Texto',
chat: 'Aplicativo de Chat', 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?', deleteAppConfirmTitle: 'Excluir este aplicativo?',
deleteAppConfirmContent: 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.', '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 const translation = { // Add the Ukrainian translation object
createApp: 'Створити новий додаток', createApp: 'Створити додаток',
types: { types: {
all: 'Всі', all: 'Все',
assistant: 'Асистент', chatbot: 'Чатбот',
completion: 'Автодоповнення', agent: 'Агент',
workflow: 'Робочий Процес',
}, },
modes: { modes: {
completion: 'Генератор тексту', completion: 'Генератор тексту',
chat: 'Базовий асистент', chat: 'Базовий асистент',
}, },
createFromConfigFile: 'Створити додаток з файла конфігурації', duplicate: 'Дублювати',
duplicateTitle: 'Дублювати додаток',
export: 'Експортувати DSL',
createFromConfigFile: 'Створити через файл DSL',
deleteAppConfirmTitle: 'Видалити цей додаток?', deleteAppConfirmTitle: 'Видалити цей додаток?',
deleteAppConfirmContent: deleteAppConfirmContent:
'Видалення додатка є незворотнім. Користувачі більше не матимуть доступ до вашого додатка, всі конфігурації підказок та журнали будуть видалені назавжди.', 'Видалення додатка є незворотнім. Користувачі більше не матимуть доступ до вашого додатка, всі конфігурації підказок та журнали будуть видалені назавжди.',
......
...@@ -2,14 +2,18 @@ const translation = { ...@@ -2,14 +2,18 @@ const translation = {
createApp: '创建应用', createApp: '创建应用',
types: { types: {
all: '全部', all: '全部',
assistant: '助手', chatbot: '聊天助手',
completion: '文本生成', agent: 'Agent',
workflow: '工作流',
}, },
modes: { modes: {
completion: '文本生成型', completion: '文本生成型',
chat: '基础助手', chat: '基础助手',
}, },
createFromConfigFile: '通过导入应用配置文件创建', duplicate: '复制',
duplicateTitle: '复制应用',
export: '导出 DSL',
createFromConfigFile: '通过 DSL 文件创建',
deleteAppConfirmTitle: '确认删除应用?', deleteAppConfirmTitle: '确认删除应用?',
deleteAppConfirmContent: deleteAppConfirmContent:
'删除应用将无法撤销。用户将不能访问你的应用,所有 Prompt 编排配置和日志均将一并被删除。', '删除应用将无法撤销。用户将不能访问你的应用,所有 Prompt 编排配置和日志均将一并被删除。',
......
import type { App, AppTemplate, SiteConfig } from '@/types/app' import type { App, AppTemplate, SiteConfig } from '@/types/app'
export type AppMode = 'chat' | 'completion'
/* export type App = { /* export type App = {
id: string id: string
name: string name: string
......
import type { AppMode } from './app' import type { AppMode } from '@/types/app'
export type AppBasicInfo = { export type AppBasicInfo = {
id: string id: string
name: string name: string
......
...@@ -44,7 +44,7 @@ export type VariableInput = { ...@@ -44,7 +44,7 @@ export type VariableInput = {
/** /**
* App modes * App modes
*/ */
export const AppModes = ['completion', 'chat'] as const export const AppModes = ['agent', 'chat', 'workflow'] as const
export type AppMode = typeof AppModes[number] export type AppMode = typeof AppModes[number]
/** /**
...@@ -286,7 +286,6 @@ export type App = { ...@@ -286,7 +286,6 @@ export type App = {
/** Mode */ /** Mode */
mode: AppMode mode: AppMode
is_agent: boolean
/** Enable web app */ /** Enable web app */
enable_site: boolean enable_site: boolean
/** Enable web API */ /** 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