mirror of
https://github.com/langgenius/dify.git
synced 2026-05-11 23:18:39 +08:00
feat: add description and tag filter
This commit is contained in:
parent
39b1062c20
commit
8e47e8dbf1
@ -58,7 +58,7 @@ describe('Category', () => {
|
||||
renderComponent({ value: 'Unknown' })
|
||||
|
||||
const allCategoriesItem = screen.getByText('explore.apps.allCategories')
|
||||
expect(allCategoriesItem.className).toContain('bg-components-main-nav-nav-button-bg-active')
|
||||
expect(allCategoriesItem.className).toContain('bg-background-default')
|
||||
})
|
||||
|
||||
it('should render raw category name when i18n key does not exist', () => {
|
||||
|
||||
@ -201,6 +201,7 @@ describe('AppList', () => {
|
||||
|
||||
expect(screen.getByText('Alpha')).toBeInTheDocument()
|
||||
expect(screen.getByText('Beta')).toBeInTheDocument()
|
||||
expect(screen.getByText('explore.apps.description')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render continue work placeholders', () => {
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
|
||||
import type { App } from '@/models/explore'
|
||||
import type { TryAppSelection } from '@/types/try-app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useDebounceFn } from 'ahooks'
|
||||
@ -197,36 +196,35 @@ const Apps = ({
|
||||
<LearnDify className="mt-4" />
|
||||
|
||||
<div className="sticky top-0 z-10 bg-background-body">
|
||||
<div className={cn(
|
||||
'flex items-center justify-between px-12 pt-6',
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<div className="grow truncate system-xl-semibold text-text-primary">{!hasFilterCondition ? t('apps.title', { ns: 'explore' }) : t('apps.resultNum', { num: searchFilteredList.length, ns: 'explore' })}</div>
|
||||
{hasFilterCondition && (
|
||||
<>
|
||||
<div className="mx-3 h-4 w-px bg-divider-regular"></div>
|
||||
<Button size="medium" onClick={handleResetFilter}>{t('apps.resetFilter', { ns: 'explore' })}</Button>
|
||||
</>
|
||||
)}
|
||||
<div className="px-12 pt-4">
|
||||
<div className="flex min-w-0 flex-col gap-0.5">
|
||||
<div className="flex min-w-0 items-center">
|
||||
<div className="grow truncate system-xl-medium text-text-primary">{!hasFilterCondition ? t('apps.title', { ns: 'explore' }) : t('apps.resultNum', { num: searchFilteredList.length, ns: 'explore' })}</div>
|
||||
</div>
|
||||
<p className="truncate system-xs-regular text-text-tertiary">
|
||||
{t('apps.description', { ns: 'explore' })}
|
||||
</p>
|
||||
</div>
|
||||
<Input
|
||||
showLeftIcon
|
||||
showClearIcon
|
||||
wrapperClassName="w-[200px] self-start"
|
||||
value={keywords}
|
||||
onChange={e => handleKeywordsChange(e.target.value)}
|
||||
onClear={() => handleKeywordsChange('')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="px-12 pt-2 pb-4">
|
||||
<div className="flex items-end justify-between gap-4 px-12 pt-3 pb-3">
|
||||
<Category
|
||||
className="min-w-0"
|
||||
list={categories}
|
||||
value={currCategory}
|
||||
onChange={setCurrCategory}
|
||||
allCategoriesEn={allCategoriesEn}
|
||||
/>
|
||||
<div className="flex shrink-0 items-center gap-3">
|
||||
<Input
|
||||
showLeftIcon
|
||||
showClearIcon
|
||||
wrapperClassName="w-[200px] shrink-0"
|
||||
value={keywords}
|
||||
onChange={e => handleKeywordsChange(e.target.value)}
|
||||
onClear={handleResetFilter}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@ import type { AppCategory } from '@/models/explore'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ThumbsUp } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
|
||||
import exploreI18n from '@/i18n/en-US/explore.json'
|
||||
|
||||
type ICategoryProps = {
|
||||
@ -29,8 +28,8 @@ const Category: FC<ICategoryProps> = ({
|
||||
const isAllCategories = !list.includes(value as AppCategory) || value === allCategoriesEn
|
||||
|
||||
const itemClassName = (isSelected: boolean) => cn(
|
||||
'flex h-7 cursor-pointer items-center rounded-lg border border-transparent px-3 system-sm-medium text-text-tertiary hover:bg-components-main-nav-nav-button-bg-active',
|
||||
isSelected && 'border-components-main-nav-nav-button-border bg-components-main-nav-nav-button-bg-active text-components-main-nav-nav-button-text-active shadow-xs',
|
||||
'flex h-7 cursor-pointer items-center justify-center gap-0.5 rounded-lg border-[0.5px] border-transparent px-2 py-1 system-sm-medium text-text-secondary hover:bg-state-base-hover-alt',
|
||||
isSelected && 'border-components-main-nav-nav-button-border bg-background-default text-text-accent-light-mode-only shadow-xs hover:bg-background-default',
|
||||
)
|
||||
|
||||
const renderCategoryName = (name: AppCategory) => {
|
||||
@ -39,23 +38,36 @@ const Category: FC<ICategoryProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'flex flex-wrap gap-1 text-[13px]')}>
|
||||
<div
|
||||
className={itemClassName(isAllCategories)}
|
||||
onClick={() => onChange(allCategoriesEn)}
|
||||
>
|
||||
<ThumbsUp className="mr-1 h-3.5 w-3.5" />
|
||||
{t('apps.allCategories', { ns: 'explore' })}
|
||||
</div>
|
||||
{list.filter(name => name !== allCategoriesEn).map(name => (
|
||||
<div
|
||||
key={name}
|
||||
className={itemClassName(name === value)}
|
||||
onClick={() => onChange(name)}
|
||||
>
|
||||
{renderCategoryName(name)}
|
||||
</div>
|
||||
))}
|
||||
<div className={cn(className, 'inline-flex max-w-full flex-wrap items-center gap-px rounded-[10px] bg-components-input-bg-normal p-0.5 text-[13px]')}>
|
||||
{[
|
||||
{ name: allCategoriesEn, label: t('apps.allCategories', { ns: 'explore' }), isAll: true },
|
||||
...list.filter(name => name !== allCategoriesEn).map(name => ({
|
||||
name,
|
||||
label: renderCategoryName(name),
|
||||
isAll: false,
|
||||
})),
|
||||
].map((item, index, items) => {
|
||||
const isSelected = item.isAll ? isAllCategories : item.name === value
|
||||
const nextItem = items[index + 1]
|
||||
const isNextSelected = nextItem
|
||||
? nextItem.isAll ? isAllCategories : nextItem.name === value
|
||||
: false
|
||||
|
||||
return (
|
||||
<div key={item.isAll ? 'all' : item.name} className="relative flex items-center">
|
||||
<div
|
||||
className={itemClassName(isSelected)}
|
||||
onClick={() => onChange(item.name)}
|
||||
>
|
||||
{item.isAll && <span className="i-custom-vender-line-alertsAndFeedback-thumbs-up h-4 w-4" />}
|
||||
{item.label}
|
||||
</div>
|
||||
{!isSelected && !isNextSelected && index < items.length - 1 && (
|
||||
<div className="absolute top-1/2 right-[-1px] h-3.5 w-px -translate-y-1/2 bg-divider-regular" />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "أيقونة التطبيق واسمه",
|
||||
"appCustomize.title": "إنشاء تطبيق من {{name}}",
|
||||
"apps.allCategories": "موصى به",
|
||||
"apps.description": "قوالب جاهزة للاستخدام من المجتمع وفريق Dify.",
|
||||
"apps.resetFilter": "مسح الفلتر",
|
||||
"apps.resultNum": "{{num}} نتائج",
|
||||
"apps.title": "قوالب",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "App-Symbol & Name",
|
||||
"appCustomize.title": "App aus {{name}} erstellen",
|
||||
"apps.allCategories": "Alle",
|
||||
"apps.description": "Sofort nutzbare Vorlagen aus der Community und vom Dify-Team.",
|
||||
"apps.resetFilter": "Filter löschen",
|
||||
"apps.resultNum": "{{num}} Ergebnisse",
|
||||
"apps.title": "Vorlagen",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "App icon & name",
|
||||
"appCustomize.title": "Create app from {{name}}",
|
||||
"apps.allCategories": "All",
|
||||
"apps.description": "Ready-to-use templates from the community and Dify team.",
|
||||
"apps.resetFilter": "Clear filter",
|
||||
"apps.resultNum": "{{num}} results",
|
||||
"apps.title": "Templates",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Icono y nombre de la aplicación",
|
||||
"appCustomize.title": "Crear aplicación a partir de {{name}}",
|
||||
"apps.allCategories": "Todos",
|
||||
"apps.description": "Plantillas listas para usar de la comunidad y el equipo de Dify.",
|
||||
"apps.resetFilter": "Limpiar filtro",
|
||||
"apps.resultNum": "{{num}} resultados",
|
||||
"apps.title": "Plantillas",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "آیکون و نام برنامه",
|
||||
"appCustomize.title": "ایجاد برنامه از {{name}}",
|
||||
"apps.allCategories": "همه",
|
||||
"apps.description": "الگوهای آماده استفاده از جامعه و تیم Dify.",
|
||||
"apps.resetFilter": "پاک کردن فیلتر",
|
||||
"apps.resultNum": "{{num}} نتیجه",
|
||||
"apps.title": "الگوها",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Icône de l'application & nom",
|
||||
"appCustomize.title": "Créer une application à partir de {{name}}",
|
||||
"apps.allCategories": "Tous",
|
||||
"apps.description": "Des modèles prêts à l'emploi créés par la communauté et l'équipe Dify.",
|
||||
"apps.resetFilter": "Effacer le filtre",
|
||||
"apps.resultNum": "{{num}} résultats",
|
||||
"apps.title": "Modèles",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "ऐप आइकन और नाम",
|
||||
"appCustomize.title": "{{name}} से ऐप बनाएँ",
|
||||
"apps.allCategories": "सभी",
|
||||
"apps.description": "समुदाय और Dify टीम के उपयोग के लिए तैयार टेम्पलेट।",
|
||||
"apps.resetFilter": "फ़िल्टर साफ़ करें",
|
||||
"apps.resultNum": "{{num}} परिणाम",
|
||||
"apps.title": "टेम्पलेट",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Ikon & nama aplikasi",
|
||||
"appCustomize.title": "Buat aplikasi dari {{name}}",
|
||||
"apps.allCategories": "Semua",
|
||||
"apps.description": "Templat siap pakai dari komunitas dan tim Dify.",
|
||||
"apps.resetFilter": "Hapus filter",
|
||||
"apps.resultNum": "{{num}} hasil",
|
||||
"apps.title": "Templat",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Icona & nome dell'app",
|
||||
"appCustomize.title": "Crea app da {{name}}",
|
||||
"apps.allCategories": "Tutti",
|
||||
"apps.description": "Modelli pronti all'uso dalla community e dal team Dify.",
|
||||
"apps.resetFilter": "Cancella filtro",
|
||||
"apps.resultNum": "{{num}} risultati",
|
||||
"apps.title": "Modelli",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "アプリアイコンと名前",
|
||||
"appCustomize.title": "{{name}}からアプリを作成",
|
||||
"apps.allCategories": "全て",
|
||||
"apps.description": "コミュニティと Dify チームによる、すぐに使えるテンプレート。",
|
||||
"apps.resetFilter": "クリア",
|
||||
"apps.resultNum": "{{num}}件の結果",
|
||||
"apps.title": "テンプレート",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "앱 아이콘 및 이름",
|
||||
"appCustomize.title": "{{name}}으로 앱 만들기",
|
||||
"apps.allCategories": "전체",
|
||||
"apps.description": "커뮤니티와 Dify 팀이 만든 바로 사용할 수 있는 템플릿입니다.",
|
||||
"apps.resetFilter": "필터 지우기",
|
||||
"apps.resultNum": "{{num}}개 결과",
|
||||
"apps.title": "템플릿",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "App icon & name",
|
||||
"appCustomize.title": "Create app from {{name}}",
|
||||
"apps.allCategories": "All",
|
||||
"apps.description": "Gebruiksklare sjablonen van de community en het Dify-team.",
|
||||
"apps.resetFilter": "Clear filter",
|
||||
"apps.resultNum": "{{num}} results",
|
||||
"apps.title": "Sjablonen",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Ikona i nazwa aplikacji",
|
||||
"appCustomize.title": "Utwórz aplikację z {{name}}",
|
||||
"apps.allCategories": "Wszystkie",
|
||||
"apps.description": "Gotowe do użycia szablony od społeczności i zespołu Dify.",
|
||||
"apps.resetFilter": "Wyczyść filtr",
|
||||
"apps.resultNum": "{{num}} wyników",
|
||||
"apps.title": "Szablony",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Ícone e nome do aplicativo",
|
||||
"appCustomize.title": "Criar aplicativo a partir de {{name}}",
|
||||
"apps.allCategories": "Todos",
|
||||
"apps.description": "Modelos prontos para uso da comunidade e da equipe Dify.",
|
||||
"apps.resetFilter": "Limpar filtro",
|
||||
"apps.resultNum": "{{num}} resultados",
|
||||
"apps.title": "Modelos",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Pictogramă și nume aplicație",
|
||||
"appCustomize.title": "Creați o aplicație din {{name}}",
|
||||
"apps.allCategories": "Toate",
|
||||
"apps.description": "Șabloane gata de utilizare de la comunitate și echipa Dify.",
|
||||
"apps.resetFilter": "Șterge filtrul",
|
||||
"apps.resultNum": "{{num}} rezultate",
|
||||
"apps.title": "Șabloane",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Значок и название приложения",
|
||||
"appCustomize.title": "Создать приложение из {{name}}",
|
||||
"apps.allCategories": "Все",
|
||||
"apps.description": "Готовые к использованию шаблоны от сообщества и команды Dify.",
|
||||
"apps.resetFilter": "Очистить фильтр",
|
||||
"apps.resultNum": "{{num}} результатов",
|
||||
"apps.title": "Шаблоны",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Ikona aplikacije & ime",
|
||||
"appCustomize.title": "Ustvari aplikacijo iz {{name}}",
|
||||
"apps.allCategories": "Vse",
|
||||
"apps.description": "Predloge, pripravljene za uporabo, iz skupnosti in ekipe Dify.",
|
||||
"apps.resetFilter": "Počisti filter",
|
||||
"apps.resultNum": "{{num}} rezultatov",
|
||||
"apps.title": "Predloge",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "ไอคอนและชื่อแอป",
|
||||
"appCustomize.title": "สร้างแอปจาก {{name}}",
|
||||
"apps.allCategories": "ทั้งหมด",
|
||||
"apps.description": "เทมเพลตพร้อมใช้งานจากชุมชนและทีม Dify",
|
||||
"apps.resetFilter": "ล้างตัวกรอง",
|
||||
"apps.resultNum": "{{num}} ผลลัพธ์",
|
||||
"apps.title": "เทมเพลต",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Uygulama simgesi ve ismi",
|
||||
"appCustomize.title": "{{name}} uygulamasından uygulama oluştur",
|
||||
"apps.allCategories": "Tümü",
|
||||
"apps.description": "Topluluk ve Dify ekibinden kullanıma hazır şablonlar.",
|
||||
"apps.resetFilter": "Filtreyi temizle",
|
||||
"apps.resultNum": "{{num}} sonuç",
|
||||
"apps.title": "Şablonlar",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Значок програми та назва",
|
||||
"appCustomize.title": "Створити додаток з {{name}}",
|
||||
"apps.allCategories": "Всі",
|
||||
"apps.description": "Готові до використання шаблони від спільноти та команди Dify.",
|
||||
"apps.resetFilter": "Очистити фільтр",
|
||||
"apps.resultNum": "{{num}} результатів",
|
||||
"apps.title": "Шаблони",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "Biểu tượng và tên ứng dụng",
|
||||
"appCustomize.title": "Tạo ứng dụng từ {{name}}",
|
||||
"apps.allCategories": "Tất cả",
|
||||
"apps.description": "Các mẫu sẵn sàng sử dụng từ cộng đồng và đội ngũ Dify.",
|
||||
"apps.resetFilter": "Xóa bộ lọc",
|
||||
"apps.resultNum": "{{num}} kết quả",
|
||||
"apps.title": "Mẫu",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "应用程序图标和名称",
|
||||
"appCustomize.title": "从 {{name}} 创建应用程序",
|
||||
"apps.allCategories": "所有",
|
||||
"apps.description": "来自社区和 Dify 团队的开箱即用模板。",
|
||||
"apps.resetFilter": "清除筛选",
|
||||
"apps.resultNum": "{{num}} 个结果",
|
||||
"apps.title": "模板",
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"appCustomize.subTitle": "應用程式圖示和名稱",
|
||||
"appCustomize.title": "從 {{name}} 建立應用程式",
|
||||
"apps.allCategories": "全部",
|
||||
"apps.description": "來自社群和 Dify 團隊的即用範本。",
|
||||
"apps.resetFilter": "清除篩選",
|
||||
"apps.resultNum": "{{num}} 個結果",
|
||||
"apps.title": "範本",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user