mirror of
https://github.com/langgenius/dify.git
synced 2026-05-09 21:28:25 +08:00
feat: continue work
This commit is contained in:
parent
0c387f8bc4
commit
e2411ab4ee
@ -47,6 +47,13 @@ vi.mock('@/hooks/use-import-dsl', () => ({
|
||||
isFetching: false,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/use-format-time-from-now', () => ({
|
||||
useFormatTimeFromNow: () => ({
|
||||
formatTimeFromNow: () => '3 minutes ago',
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/create-app-tracking', () => ({
|
||||
trackCreateApp: (...args: unknown[]) => mockTrackCreateApp(...args),
|
||||
}))
|
||||
@ -193,6 +200,22 @@ describe('AppList', () => {
|
||||
expect(screen.getByText('Alpha')).toBeInTheDocument()
|
||||
expect(screen.getByText('Beta')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render continue work placeholders', () => {
|
||||
mockExploreData = {
|
||||
categories: ['Writing'],
|
||||
allList: [createApp()],
|
||||
}
|
||||
|
||||
renderAppList()
|
||||
|
||||
expect(screen.getByRole('heading', { name: 'explore.continueWork.title' })).toBeInTheDocument()
|
||||
expect(screen.getByText('Automated Email Reply')).toBeInTheDocument()
|
||||
expect(screen.getByText('Customer Feedback Summary')).toBeInTheDocument()
|
||||
expect(screen.getAllByText('Evan')).toHaveLength(5)
|
||||
expect(screen.getAllByText('explore.continueWork.editedAt:{"time":"3 minutes ago"}')).toHaveLength(5)
|
||||
expect(screen.getByRole('link', { name: 'explore.continueWork.exploreStudio' })).toHaveAttribute('href', '/apps')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Props', () => {
|
||||
|
||||
@ -17,6 +17,7 @@ import Loading from '@/app/components/base/loading'
|
||||
import AppCard from '@/app/components/explore/app-card'
|
||||
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 { useAppContext } from '@/context/app-context'
|
||||
import { useImportDSL } from '@/hooks/use-import-dsl'
|
||||
@ -191,6 +192,7 @@ const Apps = ({
|
||||
<Banner />
|
||||
</div>
|
||||
)}
|
||||
<ContinueWork className="mt-10" />
|
||||
|
||||
<div className="sticky top-0 z-10 bg-background-body">
|
||||
<div className={cn(
|
||||
|
||||
61
web/app/components/explore/continue-work/data.ts
Normal file
61
web/app/components/explore/continue-work/data.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
|
||||
export type ContinueWorkItem = {
|
||||
id: string
|
||||
title: string
|
||||
author: string
|
||||
updatedAt: number
|
||||
emoji: string
|
||||
avatarClassName: string
|
||||
mode: AppModeEnum
|
||||
}
|
||||
|
||||
const currentTime = Date.now()
|
||||
|
||||
export const continueWorkItems: ContinueWorkItem[] = [
|
||||
{
|
||||
id: 'automated-email-reply',
|
||||
title: 'Automated Email Reply',
|
||||
author: 'Evan',
|
||||
updatedAt: currentTime - 30 * 1000,
|
||||
emoji: '🕹️',
|
||||
avatarClassName: 'bg-components-icon-bg-pink-soft',
|
||||
mode: AppModeEnum.CHAT,
|
||||
},
|
||||
{
|
||||
id: 'feature-request-copilot',
|
||||
title: 'Dify Feature Request Copilot',
|
||||
author: 'Evan',
|
||||
updatedAt: currentTime - 3 * 60 * 1000,
|
||||
emoji: '🪼',
|
||||
avatarClassName: 'bg-components-icon-bg-blue-soft',
|
||||
mode: AppModeEnum.CHAT,
|
||||
},
|
||||
{
|
||||
id: 'book-translation',
|
||||
title: 'Book Translation',
|
||||
author: 'Evan',
|
||||
updatedAt: currentTime - 2 * 60 * 60 * 1000,
|
||||
emoji: '📙',
|
||||
avatarClassName: 'bg-components-icon-bg-orange-dark-soft',
|
||||
mode: AppModeEnum.WORKFLOW,
|
||||
},
|
||||
{
|
||||
id: 'svg-logo-design',
|
||||
title: 'SVG Logo Design',
|
||||
author: 'Evan',
|
||||
updatedAt: currentTime - 24 * 60 * 60 * 1000,
|
||||
emoji: '🖌️',
|
||||
avatarClassName: 'bg-components-icon-bg-indigo-soft',
|
||||
mode: AppModeEnum.AGENT_CHAT,
|
||||
},
|
||||
{
|
||||
id: 'customer-feedback-summary',
|
||||
title: 'Customer Feedback Summary',
|
||||
author: 'Evan',
|
||||
updatedAt: currentTime - 5 * 24 * 60 * 60 * 1000,
|
||||
emoji: '📊',
|
||||
avatarClassName: 'bg-components-icon-bg-teal-soft',
|
||||
mode: AppModeEnum.COMPLETION,
|
||||
},
|
||||
]
|
||||
71
web/app/components/explore/continue-work/index.tsx
Normal file
71
web/app/components/explore/continue-work/index.tsx
Normal file
@ -0,0 +1,71 @@
|
||||
'use client'
|
||||
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AppTypeIcon } from '@/app/components/app/type-selector'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import Link from '@/next/link'
|
||||
import { continueWorkItems } from './data'
|
||||
|
||||
type ContinueWorkProps = {
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ContinueWork = ({
|
||||
className,
|
||||
}: ContinueWorkProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { formatTimeFromNow } = useFormatTimeFromNow()
|
||||
|
||||
return (
|
||||
<section className={cn('px-12', className)} aria-labelledby="continue-work-title">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 id="continue-work-title" className="min-w-0 truncate system-xl-semibold text-text-primary">
|
||||
{t('continueWork.title', { ns: 'explore' })}
|
||||
</h2>
|
||||
<Link
|
||||
href="/apps"
|
||||
className="ml-4 shrink-0 system-sm-medium text-text-accent"
|
||||
>
|
||||
{t('continueWork.exploreStudio', { ns: 'explore' })}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-3 pt-2 sm:grid-cols-2 xl:grid-cols-4">
|
||||
{continueWorkItems.map(item => (
|
||||
<article
|
||||
key={item.id}
|
||||
className="flex min-w-0 items-center gap-3 overflow-hidden rounded-xl border-[0.5px] border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-4 pt-4 pb-3 shadow-md"
|
||||
>
|
||||
<div className="relative shrink-0">
|
||||
<div className={cn(
|
||||
'flex size-10 items-center justify-center rounded-lg border-[0.5px] border-divider-regular text-2xl leading-none',
|
||||
item.avatarClassName,
|
||||
)}
|
||||
>
|
||||
<span aria-hidden>{item.emoji}</span>
|
||||
</div>
|
||||
<AppTypeIcon
|
||||
type={item.mode}
|
||||
wrapperClassName="absolute -right-0.5 -bottom-0.5 size-4 rounded-xs border-components-panel-on-panel-item-bg shadow-sm"
|
||||
className="size-3"
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0 py-px">
|
||||
<h3 className="truncate system-md-semibold text-text-secondary" title={item.title}>
|
||||
{item.title}
|
||||
</h3>
|
||||
<div className="flex min-w-0 items-center gap-1 system-xs-regular text-text-tertiary">
|
||||
<span className="shrink-0">{item.author}</span>
|
||||
<span className="shrink-0">·</span>
|
||||
<span className="min-w-0 truncate">{t('continueWork.editedAt', { ns: 'explore', time: formatTimeFromNow(item.updatedAt) })}</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(ContinueWork)
|
||||
@ -20,6 +20,9 @@
|
||||
"category.Translate": "Translate",
|
||||
"category.Workflow": "Workflow",
|
||||
"category.Writing": "Writing",
|
||||
"continueWork.editedAt": "Edited {{time}}",
|
||||
"continueWork.exploreStudio": "Explore Studio →",
|
||||
"continueWork.title": "Continue work with",
|
||||
"sidebar.action.delete": "Delete",
|
||||
"sidebar.action.pin": "Pin",
|
||||
"sidebar.action.rename": "Rename",
|
||||
|
||||
@ -20,6 +20,9 @@
|
||||
"category.Translate": "翻译",
|
||||
"category.Workflow": "工作流",
|
||||
"category.Writing": "写作",
|
||||
"continueWork.editedAt": "{{time}}编辑",
|
||||
"continueWork.exploreStudio": "探索工作室 →",
|
||||
"continueWork.title": "继续处理",
|
||||
"sidebar.action.delete": "删除",
|
||||
"sidebar.action.pin": "置顶",
|
||||
"sidebar.action.rename": "重命名",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user