Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tech
dify
Commits
804a0904
Commit
804a0904
authored
Feb 24, 2024
by
JzoNg
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
app templates
parent
14cfb310
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
85 additions
and
273 deletions
+85
-273
NewAppDialog.tsx
web/app/(commonLayout)/apps/NewAppDialog.tsx
+0
-234
appForm.tsx
web/app/components/app/create-app-dialog/appForm.tsx
+2
-2
index.tsx
web/app/components/app/create-app-dialog/index.tsx
+8
-6
index.tsx
web/app/components/explore/app-card/index.tsx
+20
-12
index.tsx
web/app/components/explore/app-list/index.tsx
+35
-11
category.tsx
web/app/components/explore/category.tsx
+5
-4
index.tsx
web/app/components/explore/create-app-modal/index.tsx
+1
-0
index.tsx
web/app/components/header/app-nav/index.tsx
+6
-2
index.tsx
web/app/components/header/app-selector/index.tsx
+6
-2
app.ts
web/i18n/en-US/app.ts
+1
-0
app.ts
web/i18n/zh-Hans/app.ts
+1
-0
No files found.
web/app/(commonLayout)/apps/NewAppDialog.tsx
deleted
100644 → 0
View file @
14cfb310
'use client'
import
type
{
MouseEventHandler
}
from
'react'
import
{
useCallback
,
useEffect
,
useRef
,
useState
}
from
'react'
import
useSWR
from
'swr'
import
classNames
from
'classnames'
import
{
useRouter
}
from
'next/navigation'
import
{
useContext
,
useContextSelector
}
from
'use-context-selector'
import
{
useTranslation
}
from
'react-i18next'
import
style
from
'../list.module.css'
import
AppModeLabel
from
'./AppModeLabel'
import
Button
from
'@/app/components/base/button'
import
Dialog
from
'@/app/components/base/dialog'
import
type
{
AppMode
}
from
'@/types/app'
import
{
ToastContext
}
from
'@/app/components/base/toast'
import
{
createApp
,
fetchAppTemplates
}
from
'@/service/apps'
import
AppIcon
from
'@/app/components/base/app-icon'
import
AppsContext
,
{
useAppContext
}
from
'@/context/app-context'
import
EmojiPicker
from
'@/app/components/base/emoji-picker'
import
{
useProviderContext
}
from
'@/context/provider-context'
import
AppsFull
from
'@/app/components/billing/apps-full-in-dialog'
import
{
AiText
}
from
'@/app/components/base/icons/src/vender/solid/communication'
type
NewAppDialogProps
=
{
show
:
boolean
onSuccess
?:
()
=>
void
onClose
?:
()
=>
void
}
const
NewAppDialog
=
({
show
,
onSuccess
,
onClose
}:
NewAppDialogProps
)
=>
{
const
router
=
useRouter
()
const
{
notify
}
=
useContext
(
ToastContext
)
const
{
isCurrentWorkspaceManager
}
=
useAppContext
()
const
{
t
}
=
useTranslation
()
const
nameInputRef
=
useRef
<
HTMLInputElement
>
(
null
)
const
[
newAppMode
,
setNewAppMode
]
=
useState
<
AppMode
>
()
const
[
isWithTemplate
,
setIsWithTemplate
]
=
useState
(
false
)
const
[
selectedTemplateIndex
,
setSelectedTemplateIndex
]
=
useState
<
number
>
(
-
1
)
// Emoji Picker
const
[
showEmojiPicker
,
setShowEmojiPicker
]
=
useState
(
false
)
const
[
emoji
,
setEmoji
]
=
useState
({
icon
:
'🤖'
,
icon_background
:
'#FFEAD5'
})
const
mutateApps
=
useContextSelector
(
AppsContext
,
state
=>
state
.
mutateApps
)
const
{
data
:
templates
,
mutate
}
=
useSWR
({
url
:
'/app-templates'
},
fetchAppTemplates
)
const
mutateTemplates
=
useCallback
(
()
=>
mutate
(),
[],
)
useEffect
(()
=>
{
if
(
show
)
{
mutateTemplates
()
setIsWithTemplate
(
false
)
}
},
[
mutateTemplates
,
show
])
const
{
plan
,
enableBilling
}
=
useProviderContext
()
const
isAppsFull
=
(
enableBilling
&&
plan
.
usage
.
buildApps
>=
plan
.
total
.
buildApps
)
const
isCreatingRef
=
useRef
(
false
)
const
onCreate
:
MouseEventHandler
=
useCallback
(
async
()
=>
{
const
name
=
nameInputRef
.
current
?.
value
if
(
!
name
)
{
notify
({
type
:
'error'
,
message
:
t
(
'app.newApp.nameNotEmpty'
)
})
return
}
if
(
!
templates
||
(
isWithTemplate
&&
!
(
selectedTemplateIndex
>
-
1
)))
{
notify
({
type
:
'error'
,
message
:
t
(
'app.newApp.appTemplateNotSelected'
)
})
return
}
if
(
!
isWithTemplate
&&
!
newAppMode
)
{
notify
({
type
:
'error'
,
message
:
t
(
'app.newApp.appTypeRequired'
)
})
return
}
if
(
isCreatingRef
.
current
)
return
isCreatingRef
.
current
=
true
try
{
const
app
=
await
createApp
({
name
,
icon
:
emoji
.
icon
,
icon_background
:
emoji
.
icon_background
,
mode
:
isWithTemplate
?
templates
.
data
[
selectedTemplateIndex
].
mode
:
newAppMode
!
,
config
:
isWithTemplate
?
templates
.
data
[
selectedTemplateIndex
].
model_config
:
undefined
,
})
if
(
onSuccess
)
onSuccess
()
if
(
onClose
)
onClose
()
notify
({
type
:
'success'
,
message
:
t
(
'app.newApp.appCreated'
)
})
mutateApps
()
router
.
push
(
`/app/
${
app
.
id
}
/
${
isCurrentWorkspaceManager
?
'configuration'
:
'overview'
}
`
)
}
catch
(
e
)
{
notify
({
type
:
'error'
,
message
:
t
(
'app.newApp.appCreateFailed'
)
})
}
isCreatingRef
.
current
=
false
},
[
isWithTemplate
,
newAppMode
,
notify
,
router
,
templates
,
selectedTemplateIndex
,
emoji
])
return
<>
{
showEmojiPicker
&&
<
EmojiPicker
onSelect=
{
(
icon
,
icon_background
)
=>
{
setEmoji
({
icon
,
icon_background
})
setShowEmojiPicker
(
false
)
}
}
onClose=
{
()
=>
{
setEmoji
({
icon
:
'🤖'
,
icon_background
:
'#FFEAD5'
})
setShowEmojiPicker
(
false
)
}
}
/>
}
<
Dialog
show=
{
show
}
title=
{
t
(
'app.newApp.startToCreate'
)
}
footer=
{
<>
<
Button
onClick=
{
onClose
}
>
{
t
(
'app.newApp.Cancel'
)
}
</
Button
>
<
Button
disabled=
{
isAppsFull
}
type=
"primary"
onClick=
{
onCreate
}
>
{
t
(
'app.newApp.Create'
)
}
</
Button
>
</>
}
>
<
div
className=
'overflow-y-auto'
>
<
div
className=
{
style
.
newItemCaption
}
>
<
h3
className=
'inline'
>
{
t
(
'app.newApp.captionAppType'
)
}
</
h3
>
{
isWithTemplate
&&
(
<>
<
span
className=
'block ml-[9px] mr-[9px] w-[1px] h-[13px] bg-gray-200'
/>
<
span
className=
'inline-flex items-center gap-1 text-xs font-medium cursor-pointer text-primary-600'
onClick=
{
()
=>
setIsWithTemplate
(
false
)
}
>
{
t
(
'app.newApp.hideTemplates'
)
}
</
span
>
</>
)
}
</
div
>
{
!
isWithTemplate
&&
(
(
<>
<
ul
className=
'grid grid-cols-1 md:grid-cols-2 gap-4'
>
<
li
className=
{
classNames
(
style
.
listItem
,
style
.
selectable
,
newAppMode
===
'chat'
&&
style
.
selected
)
}
onClick=
{
()
=>
setNewAppMode
(
'chat'
)
}
>
<
div
className=
{
style
.
listItemTitle
}
>
<
span
className=
{
style
.
newItemIcon
}
>
<
span
className=
{
classNames
(
style
.
newItemIconImage
,
style
.
newItemIconChat
)
}
/>
</
span
>
<
div
className=
{
style
.
listItemHeading
}
>
<
div
className=
{
style
.
listItemHeadingContent
}
>
{
t
(
'app.newApp.chatApp'
)
}
</
div
>
</
div
>
<
div
className=
'flex items-center h-[18px] border border-indigo-300 px-1 rounded-[5px] text-xs font-medium text-indigo-600 uppercase truncate'
>
{
t
(
'app.newApp.agentAssistant'
)
}
</
div
>
</
div
>
<
div
className=
{
`${style.listItemDescription} ${style.noClip}`
}
>
{
t
(
'app.newApp.chatAppIntro'
)
}
</
div
>
{
/* <div className={classNames(style.listItemFooter, 'justify-end')}>
<a className={style.listItemLink} href='https://udify.app/chat/7CQBa5yyvYLSkZtx' target='_blank' rel='noopener noreferrer'>{t('app.newApp.previewDemo')}<span className={classNames(style.linkIcon, style.grayLinkIcon)} /></a>
</div> */
}
</
li
>
<
li
className=
{
classNames
(
style
.
listItem
,
style
.
selectable
,
newAppMode
===
'completion'
&&
style
.
selected
)
}
onClick=
{
()
=>
setNewAppMode
(
'completion'
)
}
>
<
div
className=
{
style
.
listItemTitle
}
>
<
span
className=
{
style
.
newItemIcon
}
>
{
/* <span className={classNames(style.newItemIconImage, style.newItemIconComplete)} /> */
}
<
AiText
className=
{
classNames
(
'w-5 h-5'
,
newAppMode
===
'completion'
?
'text-[#155EEF]'
:
'text-gray-700'
)
}
/>
</
span
>
<
div
className=
{
style
.
listItemHeading
}
>
<
div
className=
{
style
.
listItemHeadingContent
}
>
{
t
(
'app.newApp.completeApp'
)
}
</
div
>
</
div
>
</
div
>
<
div
className=
{
`${style.listItemDescription} ${style.noClip}`
}
>
{
t
(
'app.newApp.completeAppIntro'
)
}
</
div
>
</
li
>
</
ul
>
</>
)
)
}
{
isWithTemplate
&&
(
<
ul
className=
'grid grid-cols-1 md:grid-cols-2 gap-4'
>
{
templates
?.
data
?.
map
((
template
,
index
)
=>
(
<
li
key=
{
index
}
className=
{
classNames
(
style
.
listItem
,
style
.
selectable
,
selectedTemplateIndex
===
index
&&
style
.
selected
)
}
onClick=
{
()
=>
setSelectedTemplateIndex
(
index
)
}
>
<
div
className=
{
style
.
listItemTitle
}
>
<
AppIcon
size=
'small'
/>
<
div
className=
{
style
.
listItemHeading
}
>
<
div
className=
{
style
.
listItemHeadingContent
}
>
{
template
.
name
}
</
div
>
</
div
>
</
div
>
<
div
className=
{
style
.
listItemDescription
}
>
{
template
.
model_config
?.
pre_prompt
}
</
div
>
<
div
className=
'inline-block pl-3.5'
>
<
AppModeLabel
mode=
{
template
.
mode
}
isAgent=
{
template
.
model_config
.
agent_mode
.
enabled
}
className=
'mt-2'
/>
</
div
>
</
li
>
))
}
</
ul
>
)
}
<
div
className=
'mt-8'
>
<
h3
className=
{
style
.
newItemCaption
}
>
{
t
(
'app.newApp.captionName'
)
}
</
h3
>
<
div
className=
'flex items-center justify-between gap-3'
>
<
AppIcon
size=
'large'
onClick=
{
()
=>
{
setShowEmojiPicker
(
true
)
}
}
className=
'cursor-pointer'
icon=
{
emoji
.
icon
}
background=
{
emoji
.
icon_background
}
/>
<
input
ref=
{
nameInputRef
}
className=
'h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow'
placeholder=
{
t
(
'app.appNamePlaceholder'
)
||
''
}
/>
</
div
>
</
div
>
{
!
isWithTemplate
&&
(
<
div
className=
'flex items-center h-[34px] mt-2'
>
<
span
className=
'inline-flex items-center gap-1 text-xs font-medium cursor-pointer text-primary-600'
onClick=
{
()
=>
setIsWithTemplate
(
true
)
}
>
{
t
(
'app.newApp.showTemplates'
)
}
<
span
className=
{
style
.
rightIcon
}
/>
</
span
>
</
div
>
)
}
</
div
>
{
isAppsFull
&&
<
AppsFull
loc=
'app-create'
/>
}
</
Dialog
>
</>
}
export
default
NewAppDialog
web/app/components/app/create-app-dialog/appForm.tsx
View file @
804a0904
...
...
@@ -74,7 +74,7 @@ const AppForm = ({
},
[
name
,
notify
,
t
,
appMode
,
emoji
.
icon
,
emoji
.
icon_background
,
description
,
onConfirm
,
onHide
,
mutateApps
,
router
,
isCurrentWorkspaceManager
])
return
(
<>
<
div
className=
'overflow-y-auto'
>
{
/* app type */
}
<
div
className=
'pt-2 px-8'
>
<
div
className=
'py-2 text-sm leading-[20px] font-medium text-gray-900'
>
{
t
(
'app.newApp.captionAppType'
)
}
</
div
>
...
...
@@ -162,7 +162,7 @@ const AppForm = ({
<
Button
className=
'mr-2 text-gray-700 text-sm font-medium'
onClick=
{
onHide
}
>
{
t
(
'app.newApp.Cancel'
)
}
</
Button
>
<
Button
className=
'text-sm font-medium'
disabled=
{
isAppsFull
||
!
name
}
type=
"primary"
onClick=
{
onCreate
}
>
{
t
(
'app.newApp.Create'
)
}
</
Button
>
</
div
>
</>
</
div
>
)
}
...
...
web/app/components/app/create-app-dialog/index.tsx
View file @
804a0904
...
...
@@ -3,6 +3,7 @@
import
{
useTranslation
}
from
'react-i18next'
import
NewAppDialog
from
'./newAppDialog'
import
AppForm
from
'./appForm'
import
AppList
,
{
PageType
}
from
'@/app/components/explore/app-list'
import
{
XClose
}
from
'@/app/components/base/icons/src/vender/line/general'
type
CreateAppDialogProps
=
{
...
...
@@ -21,18 +22,19 @@ const CreateAppDialog = ({ show, onSuccess, onClose }: CreateAppDialogProps) =>
onClose=
{
()
=>
{}
}
>
{
/* blank form */
}
<
div
className=
'shrink-0
max-w-[480px] h-full bg-white overflow-y-auto
'
>
<
div
className=
'shrink-0
flex flex-col max-w-[480px] h-full bg-white
'
>
{
/* Heading */
}
<
div
className=
's
ticky top-0 pl-8 pr-6 pt-6 pb-3 rounded-ss-xl text-xl leading-[30px] font-semibold text-gray-90
0'
>
{
t
(
'app.newApp.startFromBlank'
)
}
</
div
>
<
div
className=
's
hrink-0 pl-8 pr-6 pt-6 pb-3 bg-white rounded-ss-xl text-xl leading-[30px] font-semibold text-gray-900 z-1
0'
>
{
t
(
'app.newApp.startFromBlank'
)
}
</
div
>
{
/* app form */
}
<
AppForm
onHide=
{
onClose
}
onConfirm=
{
onSuccess
}
/>
</
div
>
{
/* template list */
}
<
div
className=
'grow bg-gray-100'
>
<
div
className=
'sticky top-0 pl-8 pr-6 pt-6 pb-3 rounded-se-xl text-xl leading-[30px] font-semibold text-gray-900'
>
{
t
(
'app.newApp.startFromTemplate'
)
}
</
div
>
<
div
className=
'grow flex flex-col h-full bg-gray-100'
>
<
div
className=
'shrink-0 pl-8 pr-6 pt-6 pb-3 bg-gray-100 rounded-se-xl text-xl leading-[30px] font-semibold text-gray-900 z-10'
>
{
t
(
'app.newApp.startFromTemplate'
)
}
</
div
>
<
AppList
pageType=
{
PageType
.
CREATE
}
/>
</
div
>
<
div
className=
'absolute top-6 left-[464px] w-8 h-8 p-1 bg-white border-2 border-gray-200 rounded-2xl text-xs leading-[20px] font-medium text-gray-500 cursor-default'
>
OR
</
div
>
<
div
className=
'absolute right-6 top-6 p-2 cursor-pointer'
onClick=
{
onClose
}
>
<
div
className=
'absolute top-6 left-[464px] w-8 h-8 p-1 bg-white border-2 border-gray-200 rounded-2xl text-xs leading-[20px] font-medium text-gray-500 cursor-default
z-20
'
>
OR
</
div
>
<
div
className=
'absolute right-6 top-6 p-2 cursor-pointer
z-20
'
onClick=
{
onClose
}
>
<
XClose
className=
'w-4 h-4 text-gray-500'
/>
</
div
>
</
NewAppDialog
>
...
...
web/app/components/explore/app-card/index.tsx
View file @
804a0904
...
...
@@ -12,15 +12,17 @@ export type AppCardProps = {
app
:
App
canCreate
:
boolean
onCreate
:
()
=>
void
isExplore
:
boolean
}
const
AppCard
=
({
app
,
canCreate
,
onCreate
,
isExplore
,
}:
AppCardProps
)
=>
{
const
{
t
}
=
useTranslation
()
const
{
app
:
appBasicInfo
,
is_agent
}
=
app
const
{
app
:
appBasicInfo
}
=
app
return
(
<
div
className=
{
cn
(
s
.
wrap
,
'col-span-1 bg-white border-2 border-solid border-transparent rounded-lg shadow-sm min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
)
}
>
<
div
className=
'flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0'
>
...
...
@@ -32,18 +34,24 @@ const AppCard = ({
<
div
className=
'mb-3 px-[14px] h-9 text-xs leading-normal text-gray-500 line-clamp-2'
>
{
app
.
description
}
</
div
>
<
div
className=
'flex items-center flex-wrap min-h-[42px] px-[14px] pt-2 pb-[10px]'
>
<
div
className=
{
s
.
mode
}
>
<
AppModeLabel
mode=
{
appBasicInfo
.
mode
}
isAgent=
{
is_agent
}
/>
<
AppModeLabel
mode=
{
appBasicInfo
.
mode
}
/>
</
div
>
{
canCreate
&&
(
<
div
className=
{
cn
(
s
.
opWrap
,
'flex items-center w-full space-x-2'
)
}
>
<
Button
type=
'primary'
className=
'grow flex items-center !h-7'
onClick=
{
()
=>
onCreate
()
}
>
<
PlusIcon
className=
'w-4 h-4 mr-1'
/>
<
span
className=
'text-xs'
>
{
t
(
'explore.appCard.addToWorkspace'
)
}
</
span
>
</
Button
>
</
div
>
)
}
{
isExplore
&&
canCreate
&&
(
<
div
className=
{
cn
(
s
.
opWrap
,
'flex items-center w-full space-x-2'
)
}
>
<
Button
type=
'primary'
className=
'grow flex items-center !h-7'
onClick=
{
()
=>
onCreate
()
}
>
<
PlusIcon
className=
'w-4 h-4 mr-1'
/>
<
span
className=
'text-xs'
>
{
t
(
'explore.appCard.addToWorkspace'
)
}
</
span
>
</
Button
>
</
div
>
)
}
{
!
isExplore
&&
(
<
div
className=
{
cn
(
s
.
opWrap
,
'flex items-center w-full space-x-2'
)
}
>
<
Button
type=
'primary'
className=
'grow flex items-center !h-7'
onClick=
{
()
=>
onCreate
()
}
>
<
PlusIcon
className=
'w-4 h-4 mr-1'
/>
<
span
className=
'text-xs'
>
{
t
(
'app.newApp.useTemplate'
)
}
</
span
>
</
Button
>
</
div
>
)
}
</
div
>
</
div
>
)
...
...
web/app/components/explore/app-list/index.tsx
View file @
804a0904
'use client'
import
type
{
FC
}
from
'react'
import
React
,
{
useEffect
}
from
'react'
import
cn
from
'classnames'
import
{
useRouter
}
from
'next/navigation'
import
{
useTranslation
}
from
'react-i18next'
import
{
useContext
}
from
'use-context-selector'
...
...
@@ -19,7 +20,18 @@ import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import
{
type
AppMode
}
from
'@/types/app'
import
{
useAppContext
}
from
'@/context/app-context'
const
Apps
:
FC
=
()
=>
{
type
AppsProps
=
{
pageType
?:
PageType
}
export
enum
PageType
{
EXPLORE
=
'explore'
,
CREATE
=
'create'
,
}
const
Apps
=
({
pageType
=
PageType
.
EXPLORE
,
}:
AppsProps
)
=>
{
const
{
t
}
=
useTranslation
()
const
{
isCurrentWorkspaceManager
}
=
useAppContext
()
const
router
=
useRouter
()
...
...
@@ -81,23 +93,36 @@ const Apps: FC = () => {
}
return
(
<
div
className=
'h-full flex flex-col border-l border-gray-200'
>
<
div
className=
'shrink-0 pt-6 px-12'
>
<
div
className=
{
`mb-1 ${s.textGradient} text-xl font-semibold`
}
>
{
t
(
'explore.apps.title'
)
}
</
div
>
<
div
className=
'text-gray-500 text-sm'
>
{
t
(
'explore.apps.description'
)
}
</
div
>
</
div
>
<
div
className=
{
cn
(
'flex flex-col border-l border-gray-200'
,
pageType
===
PageType
.
EXPLORE
?
'h-full'
:
'h-[calc(100%-76px)]'
,
)
}
>
{
pageType
===
PageType
.
EXPLORE
&&
(
<
div
className=
'shrink-0 pt-6 px-12'
>
<
div
className=
{
`mb-1 ${s.textGradient} text-xl font-semibold`
}
>
{
t
(
'explore.apps.title'
)
}
</
div
>
<
div
className=
'text-gray-500 text-sm'
>
{
t
(
'explore.apps.description'
)
}
</
div
>
</
div
>
)
}
<
Category
className=
'mt-6 px-12'
className=
{
cn
(
pageType
===
PageType
.
EXPLORE
?
'mt-6 px-12'
:
'px-8 py-2'
)
}
list=
{
categories
}
value=
{
currCategory
}
onChange=
{
setCurrCategory
}
/>
<
div
className=
'relative flex flex-1 mt-6 pb-6 flex-col overflow-auto bg-gray-100 shrink-0 grow'
>
<
div
className=
{
cn
(
'relative flex flex-1 pb-6 flex-col overflow-auto bg-gray-100 shrink-0 grow'
,
pageType
===
PageType
.
EXPLORE
?
'mt-6'
:
'mt-0 pt-2'
,
)
}
>
<
nav
className=
{
`${s.appList} grid content-start gap-4 px-6 sm:px-12 shrink-0`
}
>
className=
{
cn
(
s
.
appList
,
'grid content-start shrink-0'
,
pageType
===
PageType
.
EXPLORE
?
'gap-4 px-6 sm:px-12'
:
'gap-3 px-8'
,
)
}
>
{
currList
.
map
(
app
=>
(
<
AppCard
key=
{
app
.
app_id
}
isExplore=
{
pageType
===
PageType
.
EXPLORE
}
app=
{
app
}
canCreate=
{
hasEditPermission
}
onCreate=
{
()
=>
{
...
...
@@ -108,7 +133,6 @@ const Apps: FC = () => {
))
}
</
nav
>
</
div
>
{
isShowCreateModal
&&
(
<
CreateAppModal
appName=
{
currApp
?.
app
.
name
||
''
}
...
...
web/app/components/explore/category.tsx
View file @
804a0904
...
...
@@ -23,13 +23,15 @@ const Category: FC<ICategoryProps> = ({
})
=>
{
const
{
t
}
=
useTranslation
()
const
itemClassName
=
(
isSelected
:
boolean
)
=>
cn
(
isSelected
?
'bg-white text-primary-600 border-gray-200 font-semibold'
:
'border-transparent font-medium'
,
'flex items-center h-7 px-3 border cursor-pointer rounded-lg'
)
const
itemStyle
=
(
isSelected
:
boolean
)
=>
isSelected
?
{
boxShadow
:
'0px 1px 2px rgba(16, 24, 40, 0.05)'
}
:
{}
const
itemClassName
=
(
isSelected
:
boolean
)
=>
cn
(
'px-3 py-[5px] h-[28px] rounded-lg border-[0.5px] border-transparent text-gray-700 font-medium leading-[18px] cursor-pointer hover:bg-gray-200'
,
isSelected
&&
'bg-white border-gray-200 shadow-xs text-primary-600 hover:bg-white'
,
)
return
(
<
div
className=
{
cn
(
className
,
'flex space-x-1 text-[13px] flex-wrap'
)
}
>
<
div
className=
{
itemClassName
(
value
===
''
)
}
style=
{
itemStyle
(
value
===
''
)
}
onClick=
{
()
=>
onChange
(
''
)
}
>
{
t
(
'explore.apps.allCategories'
)
}
...
...
@@ -38,7 +40,6 @@ const Category: FC<ICategoryProps> = ({
<
div
key=
{
name
}
className=
{
itemClassName
(
name
===
value
)
}
style=
{
itemStyle
(
name
===
value
)
}
onClick=
{
()
=>
onChange
(
name
)
}
>
{
categoryI18n
[
name
]
?
t
(
`explore.category.${name}`
)
:
name
}
...
...
web/app/components/explore/create-app-modal/index.tsx
View file @
804a0904
...
...
@@ -54,6 +54,7 @@ const CreateAppModal = ({
<
Modal
isShow=
{
show
}
onClose=
{
()
=>
{
}
}
wrapperClassName=
'z-40'
className=
{
cn
(
s
.
modal
,
'!max-w-[480px]'
,
'px-8'
)
}
>
<
span
className=
{
s
.
close
}
onClick=
{
onHide
}
/>
...
...
web/app/components/header/app-nav/index.tsx
View file @
804a0904
...
...
@@ -9,7 +9,7 @@ import { flatten } from 'lodash-es'
import
Nav
from
'../nav'
import
{
Robot
,
RobotActive
}
from
'../../base/icons/src/public/header-nav/studio'
import
{
fetchAppDetail
,
fetchAppList
}
from
'@/service/apps'
import
NewAppDialog
from
'@/app/(commonLayout)/apps/NewAppD
ialog'
import
CreateAppDialog
from
'@/app/components/app/create-app-d
ialog'
import
type
{
AppListResponse
}
from
'@/models/app'
import
{
useAppContext
}
from
'@/context/app-context'
...
...
@@ -54,7 +54,11 @@ const AppNav = () => {
onCreate=
{
()
=>
setShowNewAppDialog
(
true
)
}
onLoadmore=
{
handleLoadmore
}
/>
<
NewAppDialog
show=
{
showNewAppDialog
}
onClose=
{
()
=>
setShowNewAppDialog
(
false
)
}
/>
<
CreateAppDialog
show=
{
showNewAppDialog
}
onClose=
{
()
=>
setShowNewAppDialog
(
false
)
}
onSuccess=
{
()
=>
{}
}
/>
</>
)
}
...
...
web/app/components/header/app-selector/index.tsx
View file @
804a0904
...
...
@@ -6,7 +6,7 @@ import { Menu, Transition } from '@headlessui/react'
import
{
useRouter
}
from
'next/navigation'
import
Indicator
from
'../indicator'
import
type
{
AppDetailResponse
}
from
'@/models/app'
import
NewAppDialog
from
'@/app/(commonLayout)/apps/NewAppD
ialog'
import
CreateAppDialog
from
'@/app/components/app/create-app-d
ialog'
import
AppIcon
from
'@/app/components/base/app-icon'
import
{
useAppContext
}
from
'@/context/app-context'
...
...
@@ -101,7 +101,11 @@ export default function AppSelector({ appItems, curApp }: IAppSelectorProps) {
</
Menu
.
Items
>
</
Transition
>
</
Menu
>
<
NewAppDialog
show=
{
showNewAppDialog
}
onClose=
{
()
=>
setShowNewAppDialog
(
false
)
}
/>
<
CreateAppDialog
show=
{
showNewAppDialog
}
onClose=
{
()
=>
setShowNewAppDialog
(
false
)
}
onSuccess=
{
()
=>
{}
}
/>
</
div
>
)
}
web/i18n/en-US/app.ts
View file @
804a0904
...
...
@@ -30,6 +30,7 @@ const translation = {
appNamePlaceholder
:
'Give your app a name'
,
captionDescription
:
'Description'
,
appDescriptionPlaceholder
:
'Enter the description of the app'
,
useTemplate
:
'Use this template'
,
previewDemo
:
'Preview demo'
,
chatApp
:
'Assistant'
,
chatAppIntro
:
...
...
web/i18n/zh-Hans/app.ts
View file @
804a0904
...
...
@@ -30,6 +30,7 @@ const translation = {
appNamePlaceholder
:
'给你的应用起个名字'
,
captionDescription
:
'描述'
,
appDescriptionPlaceholder
:
'输入应用的描述'
,
useTemplate
:
'使用该模板'
,
previewDemo
:
'预览 Demo'
,
chatApp
:
'助手'
,
chatAppIntro
:
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment