Skip to content

Commit

Permalink
Feat: new entry point for app creation (#10847)
Browse files Browse the repository at this point in the history
  • Loading branch information
douxc authored Dec 13, 2024
1 parent bdfdccd commit a725b8b
Show file tree
Hide file tree
Showing 93 changed files with 1,366 additions and 590 deletions.
35 changes: 9 additions & 26 deletions web/app/(commonLayout)/apps/AppCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import Divider from '@/app/components/base/divider'
import { getRedirection } from '@/utils/app-redirection'
import { useProviderContext } from '@/context/provider-context'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
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 EditAppModal from '@/app/components/explore/create-app-modal'
import SwitchAppModal from '@/app/components/app/switch-app-modal'
Expand All @@ -32,6 +30,7 @@ import type { EnvironmentVariable } from '@/app/components/workflow/types'
import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal'
import { fetchWorkflowDraft } from '@/service/workflow'
import { fetchInstalledAppList } from '@/service/explore'
import { AppTypeIcon } from '@/app/components/app/type-selector'

export type AppCardProps = {
app: App
Expand Down Expand Up @@ -277,7 +276,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
e.preventDefault()
getRedirection(isCurrentWorkspaceEditor, app, push)
}}
className='relative group col-span-1 bg-white border-2 border-solid border-transparent rounded-xl shadow-sm flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg'
className='relative h-[160px] group col-span-1 bg-components-card-bg border-[1px] border-solid border-components-card-border rounded-xl shadow-sm inline-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'>
<div className='relative shrink-0'>
Expand All @@ -288,38 +287,22 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
background={app.icon_background}
imageUrl={app.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'>
{app.mode === 'advanced-chat' && (
<ChatBot className='w-3 h-3 text-[#1570EF]' />
)}
{app.mode === 'agent-chat' && (
<CuteRobot className='w-3 h-3 text-indigo-600' />
)}
{app.mode === 'chat' && (
<ChatBot className='w-3 h-3 text-[#1570EF]' />
)}
{app.mode === 'completion' && (
<AiText className='w-3 h-3 text-[#0E9384]' />
)}
{app.mode === 'workflow' && (
<Route className='w-3 h-3 text-[#f79009]' />
)}
</span>
<AppTypeIcon type={app.mode} wrapperClassName='absolute -bottom-0.5 -right-0.5 w-4 h-4 shadow-sm' className='w-3 h-3' />
</div>
<div className='grow w-0 py-[1px]'>
<div className='flex items-center text-sm leading-5 font-semibold text-gray-800'>
<div className='flex items-center text-sm leading-5 font-semibold text-text-secondary'>
<div className='truncate' title={app.name}>{app.name}</div>
</div>
<div className='flex items-center text-[10px] leading-[18px] text-gray-500 font-medium'>
{app.mode === 'advanced-chat' && <div className='truncate'>{t('app.types.chatbot').toUpperCase()}</div>}
<div className='flex items-center text-[10px] leading-[18px] text-text-tertiary font-medium'>
{app.mode === 'advanced-chat' && <div className='truncate'>{t('app.types.advanced').toUpperCase()}</div>}
{app.mode === 'chat' && <div className='truncate'>{t('app.types.chatbot').toUpperCase()}</div>}
{app.mode === 'agent-chat' && <div className='truncate'>{t('app.types.agent').toUpperCase()}</div>}
{app.mode === 'workflow' && <div className='truncate'>{t('app.types.workflow').toUpperCase()}</div>}
{app.mode === 'completion' && <div className='truncate'>{t('app.types.completion').toUpperCase()}</div>}
</div>
</div>
</div>
<div className='title-wrapper h-[90px] px-[14px] text-xs leading-normal text-gray-500'>
<div className='title-wrapper h-[90px] px-[14px] text-xs leading-normal text-text-tertiary'>
<div
className={cn(tags.length ? 'line-clamp-2' : 'line-clamp-4', 'group-hover:line-clamp-2')}
title={app.description}
Expand Down Expand Up @@ -352,7 +335,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
/>
</div>
</div>
<div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px] bg-gray-200' />
<div className='!hidden group-hover:!flex shrink-0 mx-1 w-[1px] h-[14px]' />
<div className='!hidden group-hover:!flex shrink-0'>
<CustomPopover
htmlContent={<Operations />}
Expand All @@ -362,7 +345,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
<div
className='flex items-center justify-center w-8 h-8 cursor-pointer rounded-md'
>
<RiMoreFill className='w-4 h-4 text-gray-700' />
<RiMoreFill className='w-4 h-4 text-text-tertiary' />
</div>
}
btnClassName={open =>
Expand Down
42 changes: 33 additions & 9 deletions web/app/(commonLayout)/apps/Apps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ 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'>
<div className='sticky top-0 flex justify-between items-center pt-4 px-12 pb-2 leading-[56px] bg-background-body z-10 flex-wrap gap-y-2'>
<TabSliderNew
value={activeTab}
onChange={setActiveTab}
Expand All @@ -143,14 +143,20 @@ const Apps = () => {
/>
</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'>
{isCurrentWorkspaceEditor
&& <NewAppCard onSuccess={mutate} />}
{data?.map(({ data: apps }) => apps.map(app => (
<AppCard key={app.id} app={app} onRefresh={mutate} />
)))}
<CheckModal />
</nav>
{(data && data[0].total > 0)
? <div className='grid content-start grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6 gap-4 px-12 pt-2 grow relative'>
{isCurrentWorkspaceEditor
&& <NewAppCard onSuccess={mutate} />}
{data.map(({ data: apps }) => apps.map(app => (
<AppCard key={app.id} app={app} onRefresh={mutate} />
)))}
</div>
: <div className='grid content-start grid-cols-1 sm:grid-cols-1 md:grid-cols-2 xl:grid-cols-4 2xl:grid-cols-5 2k:grid-cols-6 gap-4 px-12 pt-2 grow relative overflow-hidden'>
{isCurrentWorkspaceEditor
&& <NewAppCard className='z-10' onSuccess={mutate} />}
<NoAppsFound />
</div>}
<CheckModal />
<div ref={anchorRef} className='h-0'> </div>
{showTagManagementModal && (
<TagManagementModal type='app' show={showTagManagementModal} />
Expand All @@ -160,3 +166,21 @@ const Apps = () => {
}

export default Apps

function NoAppsFound() {
const { t } = useTranslation()
function renderDefaultCard() {
const defaultCards = Array.from({ length: 36 }, (_, index) => (
<div key={index} className='h-[160px] inline-flex rounded-xl bg-background-default-lighter'></div>
))
return defaultCards
}
return (
<>
{renderDefaultCard()}
<div className='absolute top-0 left-0 right-0 bottom-0 flex items-center justify-center bg-gradient-to-t from-background-body to-transparent'>
<span className='system-md-medium text-text-tertiary'>{t('app.newApp.noAppsFound')}</span>
</div>
</>
)
}
28 changes: 19 additions & 9 deletions web/app/(commonLayout)/apps/NewAppCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ import CreateAppModal from '@/app/components/app/create-app-modal'
import CreateFromDSLModal, { CreateFromDSLModalTab } from '@/app/components/app/create-from-dsl-modal'
import { useProviderContext } from '@/context/provider-context'
import { FileArrow01, FilePlus01, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files'
import cn from '@/utils/classnames'

export type CreateAppCardProps = {
className?: string
onSuccess?: () => void
}

// eslint-disable-next-line react/display-name
const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuccess }, ref) => {
const CreateAppCard = forwardRef<HTMLDivElement, CreateAppCardProps>(({ className, onSuccess }, ref) => {
const { t } = useTranslation()
const { onPlanInfoChanged } = useProviderContext()
const searchParams = useSearchParams()
Expand All @@ -36,26 +38,26 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
}, [dslUrl])

return (
<a
<div
ref={ref}
className='relative col-span-1 flex flex-col justify-between min-h-[160px] bg-gray-200 rounded-xl border-[0.5px] border-black/5'
className={cn('relative col-span-1 inline-flex flex-col justify-between h-[160px] bg-components-card-bg rounded-xl border-[0.5px] border-components-card-border', className)}
>
<div className='grow p-2 rounded-t-xl'>
<div className='px-6 pt-2 pb-1 text-xs font-medium leading-[18px] text-gray-500'>{t('app.createApp')}</div>
<div className='flex items-center mb-1 px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:text-primary-600 hover:bg-white' onClick={() => setShowNewAppModal(true)}>
<div className='px-6 pt-2 pb-1 text-xs font-medium leading-[18px] text-text-tertiary'>{t('app.createApp')}</div>
<div className='flex items-center mb-1 px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover' onClick={() => setShowNewAppModal(true)}>
<FilePlus01 className='shrink-0 mr-2 w-4 h-4' />
{t('app.newApp.startFromBlank')}
</div>
<div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:text-primary-600 hover:bg-white' onClick={() => setShowNewAppTemplateDialog(true)}>
<div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover' onClick={() => setShowNewAppTemplateDialog(true)}>
<FilePlus02 className='shrink-0 mr-2 w-4 h-4' />
{t('app.newApp.startFromTemplate')}
</div>
</div>
<div
className='p-2 border-t-[0.5px] border-black/5 rounded-b-xl'
className='p-2 border-t-[0.5px] border-components-card-border rounded-b-xl'
onClick={() => setShowCreateFromDSLModal(true)}
>
<div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-gray-600 cursor-pointer hover:text-primary-600 hover:bg-white'>
<div className='flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover'>
<FileArrow01 className='shrink-0 mr-2 w-4 h-4' />
{t('app.importDSL')}
</div>
Expand All @@ -68,6 +70,10 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
if (onSuccess)
onSuccess()
}}
onCreateFromTemplate={() => {
setShowNewAppTemplateDialog(true)
setShowNewAppModal(false)
}}
/>
<CreateAppTemplateDialog
show={showNewAppTemplateDialog}
Expand All @@ -77,6 +83,10 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
if (onSuccess)
onSuccess()
}}
onCreateFromBlank={() => {
setShowNewAppModal(true)
setShowNewAppTemplateDialog(false)
}}
/>
<CreateFromDSLModal
show={showCreateFromDSLModal}
Expand All @@ -94,7 +104,7 @@ const CreateAppCard = forwardRef<HTMLAnchorElement, CreateAppCardProps>(({ onSuc
onSuccess()
}}
/>
</a>
</div>
)
})

Expand Down
15 changes: 10 additions & 5 deletions web/app/(commonLayout)/apps/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use client'
import { useContextSelector } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { RiDiscordFill, RiGithubFill } from '@remixicon/react'
import Link from 'next/link'
import style from '../list.module.css'
import Apps from './Apps'
import classNames from '@/utils/classnames'
import AppContext from '@/context/app-context'
import { LicenseStatus } from '@/types/feature'

Expand All @@ -12,14 +13,18 @@ const AppList = () => {
const systemFeatures = useContextSelector(AppContext, v => v.systemFeatures)

return (
<div className='relative flex flex-col overflow-y-auto bg-gray-100 shrink-0 h-0 grow'>
<div className='relative flex flex-col overflow-y-auto bg-background-body shrink-0 h-0 grow'>
<Apps />
{systemFeatures.license.status === LicenseStatus.NONE && <footer className='px-12 py-6 grow-0 shrink-0'>
<h3 className='text-xl font-semibold leading-tight text-gradient'>{t('app.join')}</h3>
<p className='mt-1 text-sm font-normal leading-tight text-gray-700'>{t('app.communityIntro')}</p>
<p className='mt-1 system-sm-regular text-text-tertiary'>{t('app.communityIntro')}</p>
<div className='flex items-center gap-2 mt-3'>
<a className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://github.com/langgenius/dify'><span className={classNames(style.socialMediaIcon, style.githubIcon)} /></a>
<a className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://discord.gg/FngNHpbcY7'><span className={classNames(style.socialMediaIcon, style.discordIcon)} /></a>
<Link className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://github.com/langgenius/dify'>
<RiGithubFill className='w-5 h-5 text-text-tertiary' />
</Link>
<Link className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://discord.gg/FngNHpbcY7'>
<RiDiscordFill className='w-5 h-5 text-text-tertiary' />
</Link>
</div>
</footer>}
</div >
Expand Down
8 changes: 0 additions & 8 deletions web/app/(commonLayout)/list.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,6 @@
@apply block w-6 h-6 bg-center bg-contain;
}

.githubIcon {
background-image: url("./apps/assets/github.svg");
}

.discordIcon {
background-image: url("./apps/assets/discord.svg");
}

/* #region new app dialog */
.newItemCaption {
@apply inline-flex items-center mb-2 text-sm font-medium;
Expand Down
Loading

0 comments on commit a725b8b

Please sign in to comment.