From a716d8789dd2245eb9713afb010e36c5ecd621fb Mon Sep 17 00:00:00 2001 From: JzoNg Date: Mon, 23 Mar 2026 10:45:15 +0800 Subject: [PATCH] refactor: extract snippet list components --- .../components/apps/__tests__/list.spec.tsx | 2 +- web/app/components/apps/list.tsx | 92 +------------------ .../components/apps/studio-route-switch.tsx | 44 +++++++++ .../snippets/components/snippet-card.tsx | 46 ++++++++++ .../components/snippet-create-card.tsx | 25 +++++ 5 files changed, 119 insertions(+), 90 deletions(-) create mode 100644 web/app/components/apps/studio-route-switch.tsx create mode 100644 web/app/components/snippets/components/snippet-card.tsx create mode 100644 web/app/components/snippets/components/snippet-create-card.tsx diff --git a/web/app/components/apps/__tests__/list.spec.tsx b/web/app/components/apps/__tests__/list.spec.tsx index cd4be65ea0..b2f0125e90 100644 --- a/web/app/components/apps/__tests__/list.spec.tsx +++ b/web/app/components/apps/__tests__/list.spec.tsx @@ -288,7 +288,7 @@ describe('List', () => { expect(screen.getByText('snippet.create')).toBeInTheDocument() expect(screen.getByText('Tone Rewriter')).toBeInTheDocument() expect(screen.getByText('Rewrites rough drafts into a concise, professional tone for internal stakeholder updates.')).toBeInTheDocument() - expect(screen.getByRole('link', { name: /Tone Rewriter/i })).toHaveAttribute('href', '/snippets/snippet-1') + expect(screen.getByRole('link', { name: /Tone Rewriter/i })).toHaveAttribute('href', '/snippets/snippet-1/orchestrate') expect(screen.queryByTestId('new-app-card')).not.toBeInTheDocument() expect(screen.queryByTestId('app-card-app-1')).not.toBeInTheDocument() }) diff --git a/web/app/components/apps/list.tsx b/web/app/components/apps/list.tsx index 68f4999197..92d024c5d0 100644 --- a/web/app/components/apps/list.tsx +++ b/web/app/components/apps/list.tsx @@ -2,7 +2,6 @@ import type { FC } from 'react' import type { StudioPageType } from '.' -import type { SnippetListItem } from '@/models/snippet' import type { App } from '@/types/app' import { useDebounceFn } from 'ahooks' import { useQueryState } from 'nuqs' @@ -16,10 +15,11 @@ import { useAppContext } from '@/context/app-context' import { useGlobalPublicStore } from '@/context/global-public-context' import { CheckModal } from '@/hooks/use-pay' import dynamic from '@/next/dynamic' -import Link from '@/next/link' import { useInfiniteAppList } from '@/service/use-apps' import { getSnippetListMock } from '@/service/use-snippets.mock' import { cn } from '@/utils/classnames' +import SnippetCard from '../snippets/components/snippet-card' +import SnippetCreateCard from '../snippets/components/snippet-create-card' import AppCard from './app-card' import { AppCardSkeleton } from './app-card-skeleton' import AppTypeFilter from './app-type-filter' @@ -30,6 +30,7 @@ import Footer from './footer' import useAppsQueryState from './hooks/use-apps-query-state' import { useDSLDragDrop } from './hooks/use-dsl-drag-drop' import NewAppCard from './new-app-card' +import StudioRouteSwitch from './studio-route-switch' const TagManagementModal = dynamic(() => import('@/app/components/base/tag-management'), { ssr: false, @@ -38,93 +39,6 @@ const CreateFromDSLModal = dynamic(() => import('@/app/components/app/create-fro ssr: false, }) -const StudioRouteSwitch = ({ pageType, appsLabel, snippetsLabel }: { pageType: StudioPageType, appsLabel: string, snippetsLabel: string }) => { - return ( -
- - {appsLabel} - - - {snippetsLabel} - -
- ) -} - -const SnippetCreateCard = () => { - const { t } = useTranslation('snippet') - - return ( -
-
-
{t('create')}
-
- - {t('newApp.startFromBlank', { ns: 'app' })} -
-
- - {t('importDSL', { ns: 'app' })} -
-
-
- ) -} - -const SnippetCard = ({ - snippet, -}: { - snippet: SnippetListItem -}) => { - return ( - -
- {snippet.status && ( -
- {snippet.status} -
- )} -
-
- {snippet.icon} -
-
-
- {snippet.name} -
-
-
-
-
- {snippet.description} -
-
-
- {snippet.author} - · - {snippet.updatedAt} - · - {snippet.usage} -
-
- - ) -} - type Props = { controlRefreshList?: number pageType?: StudioPageType diff --git a/web/app/components/apps/studio-route-switch.tsx b/web/app/components/apps/studio-route-switch.tsx new file mode 100644 index 0000000000..7aea1cebbd --- /dev/null +++ b/web/app/components/apps/studio-route-switch.tsx @@ -0,0 +1,44 @@ +'use client' + +import type { StudioPageType } from '.' +import Link from '@/next/link' +import { cn } from '@/utils/classnames' + +type Props = { + pageType: StudioPageType + appsLabel: string + snippetsLabel: string +} + +const StudioRouteSwitch = ({ + pageType, + appsLabel, + snippetsLabel, +}: Props) => { + return ( +
+ + {appsLabel} + + + {snippetsLabel} + +
+ ) +} + +export default StudioRouteSwitch diff --git a/web/app/components/snippets/components/snippet-card.tsx b/web/app/components/snippets/components/snippet-card.tsx new file mode 100644 index 0000000000..3b59c09e6c --- /dev/null +++ b/web/app/components/snippets/components/snippet-card.tsx @@ -0,0 +1,46 @@ +'use client' + +import type { SnippetListItem } from '@/models/snippet' +import Link from '@/next/link' + +type Props = { + snippet: SnippetListItem +} + +const SnippetCard = ({ snippet }: Props) => { + return ( + +
+ {snippet.status && ( +
+ {snippet.status} +
+ )} +
+
+ {snippet.icon} +
+
+
+ {snippet.name} +
+
+
+
+
+ {snippet.description} +
+
+
+ {snippet.author} + · + {snippet.updatedAt} + · + {snippet.usage} +
+
+ + ) +} + +export default SnippetCard diff --git a/web/app/components/snippets/components/snippet-create-card.tsx b/web/app/components/snippets/components/snippet-create-card.tsx new file mode 100644 index 0000000000..b0a88c3d17 --- /dev/null +++ b/web/app/components/snippets/components/snippet-create-card.tsx @@ -0,0 +1,25 @@ +'use client' + +import { useTranslation } from 'react-i18next' + +const SnippetCreateCard = () => { + const { t } = useTranslation('snippet') + + return ( +
+
+
{t('create')}
+
+ + {t('newApp.startFromBlank', { ns: 'app' })} +
+
+ + {t('importDSL', { ns: 'app' })} +
+
+
+ ) +} + +export default SnippetCreateCard