feat: learn dify

This commit is contained in:
Joel 2026-05-09 18:12:20 +08:00
parent 36aebdaa28
commit 374eca2504
7 changed files with 163 additions and 0 deletions

View File

@ -216,6 +216,24 @@ describe('AppList', () => {
expect(screen.getAllByText('explore.continueWork.editedAt:{"time":"3 minutes ago"}')).toHaveLength(5)
expect(screen.getByRole('link', { name: 'explore.continueWork.exploreStudio' })).toHaveAttribute('href', '/apps')
})
it('should render learn dify templates without badges or template metadata', () => {
mockExploreData = {
categories: ['Writing'],
allList: [createApp()],
}
renderAppList()
expect(screen.getByRole('heading', { name: 'explore.learnDify.title' })).toBeInTheDocument()
expect(screen.getByText('Your first Workflow - say hello to AI')).toBeInTheDocument()
expect(screen.getByText('Build a working Agent with Workflow')).toBeInTheDocument()
expect(screen.getByRole('link', { name: 'explore.learnDify.moreTemplates' })).toHaveAttribute('href', '/explore/apps')
expect(screen.queryByText('Run this first')).not.toBeInTheDocument()
expect(screen.queryByText('Then try this')).not.toBeInTheDocument()
expect(screen.queryByText('workflow')).not.toBeInTheDocument()
expect(screen.queryByText('3 min')).not.toBeInTheDocument()
})
})
describe('Props', () => {

View File

@ -19,6 +19,7 @@ import Banner from '@/app/components/explore/banner/banner'
import Category from '@/app/components/explore/category'
import ContinueWork from '@/app/components/explore/continue-work'
import CreateAppModal from '@/app/components/explore/create-app-modal'
import LearnDify from '@/app/components/explore/learn-dify'
import { useAppContext } from '@/context/app-context'
import { useImportDSL } from '@/hooks/use-import-dsl'
import {
@ -193,6 +194,7 @@ const Apps = ({
</div>
)}
<ContinueWork className="mt-10" />
<LearnDify className="mt-4" />
<div className="sticky top-0 z-10 bg-background-body">
<div className={cn(

View File

@ -0,0 +1,50 @@
import type { AppIconType } from '@/types/app'
export type LearnDifyItem = {
id: string
title: string
description: string
icon_type: AppIconType | null
icon: string
icon_background: string | null
icon_url: string | null
}
export const learnDifyItems: LearnDifyItem[] = [
{
id: 'first-workflow',
title: 'Your first Workflow - say hello to AI',
description: 'Search Notion pages and open visited ones faster. No admin access required.',
icon_type: 'emoji',
icon: '🤖',
icon_background: '#F0FDF9',
icon_url: null,
},
{
id: 'agent-with-workflow',
title: 'Build a working Agent with Workflow',
description: 'Agent node to let your AI call tools and reason - inside a full Workflow you control.',
icon_type: 'emoji',
icon: '🤖',
icon_background: '#F0FDF9',
icon_url: null,
},
{
id: 'if-else-logic',
title: 'Add logic with If/Else',
description: 'Route your workflow down different paths based on conditions.',
icon_type: 'emoji',
icon: '🤖',
icon_background: '#F0FDF9',
icon_url: null,
},
{
id: 'customer-support-bot',
title: 'Customer support bot',
description: 'Auto-reply to common questions using your own knowledge base.',
icon_type: 'emoji',
icon: '🤖',
icon_background: '#F0FDF9',
icon_url: null,
},
]

View File

@ -0,0 +1,50 @@
'use client'
import { cn } from '@langgenius/dify-ui/cn'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Link from '@/next/link'
import { learnDifyItems } from './data'
import LearnDifyItem from './item'
type LearnDifyProps = {
className?: string
}
const LearnDify = ({
className,
}: LearnDifyProps) => {
const { t } = useTranslation()
return (
<section className={cn('px-12 pb-6', className)} aria-labelledby="learn-dify-title">
<div className="flex min-h-12 items-end justify-between gap-4 pb-2">
<div className="min-w-0 flex-1">
<div className="flex items-center justify-between gap-4">
<h2 id="learn-dify-title" className="min-w-0 truncate system-xl-semibold text-text-primary">
{t('learnDify.title', { ns: 'explore' })}
</h2>
<button type="button" className="shrink-0 system-sm-medium text-text-primary">
{t('learnDify.hide', { ns: 'explore' })}
</button>
</div>
<div className="mt-1 flex items-center justify-between gap-4">
<p className="min-w-0 truncate system-xs-regular text-text-tertiary">
{t('learnDify.description', { ns: 'explore' })}
</p>
<Link href="/explore/apps" className="shrink-0 system-sm-medium text-text-accent">
{t('learnDify.moreTemplates', { ns: 'explore' })}
</Link>
</div>
</div>
</div>
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 xl:grid-cols-4">
{learnDifyItems.map(item => (
<LearnDifyItem key={item.id} item={item} />
))}
</div>
</section>
)
}
export default React.memo(LearnDify)

View File

@ -0,0 +1,35 @@
'use client'
import type { LearnDifyItem as LearnDifyItemData } from './data'
import * as React from 'react'
import AppIcon from '@/app/components/base/app-icon'
type LearnDifyItemProps = {
item: LearnDifyItemData
}
const LearnDifyItem = ({
item,
}: LearnDifyItemProps) => {
return (
<article className="flex min-w-0 flex-col overflow-hidden rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-4 py-3 shadow-md">
<div className="flex items-center gap-3">
<AppIcon
size="large"
iconType={item.icon_type}
icon={item.icon}
background={item.icon_background}
imageUrl={item.icon_url}
/>
<h3 className="line-clamp-2 min-h-10 min-w-0 flex-1 system-md-semibold text-text-secondary" title={item.title}>
{item.title}
</h3>
</div>
<p className="mt-3 line-clamp-2 min-h-8 system-xs-regular text-text-tertiary">
{item.description}
</p>
</article>
)
}
export default React.memo(LearnDifyItem)

View File

@ -23,6 +23,10 @@
"continueWork.editedAt": "Edited {{time}}",
"continueWork.exploreStudio": "Explore Studio →",
"continueWork.title": "Continue work with",
"learnDify.description": "Follow the path - or jump to whichever step you're ready for.",
"learnDify.hide": "Hide",
"learnDify.moreTemplates": "More learning templates →",
"learnDify.title": "Learn Dify",
"sidebar.action.delete": "Delete",
"sidebar.action.pin": "Pin",
"sidebar.action.rename": "Rename",

View File

@ -23,6 +23,10 @@
"continueWork.editedAt": "{{time}}编辑",
"continueWork.exploreStudio": "探索工作室 →",
"continueWork.title": "继续处理",
"learnDify.description": "按路径学习,或直接跳到你准备好的步骤。",
"learnDify.hide": "隐藏",
"learnDify.moreTemplates": "更多学习模板 →",
"learnDify.title": "学习 Dify",
"sidebar.action.delete": "删除",
"sidebar.action.pin": "置顶",
"sidebar.action.rename": "重命名",