diff --git a/web/app/(commonLayout)/snippets/[snippetId]/evaluation/page.tsx b/web/app/(commonLayout)/snippets/[snippetId]/evaluation/page.tsx
index 293945ad20..adc4bb6903 100644
--- a/web/app/(commonLayout)/snippets/[snippetId]/evaluation/page.tsx
+++ b/web/app/(commonLayout)/snippets/[snippetId]/evaluation/page.tsx
@@ -1,11 +1,11 @@
-import SnippetPage from '@/app/components/snippets'
+import SnippetEvaluationPage from '@/app/components/snippets/snippet-evaluation-page'
const Page = async (props: {
params: Promise<{ snippetId: string }>
}) => {
const { snippetId } = await props.params
- return
+ return
}
export default Page
diff --git a/web/app/(commonLayout)/snippets/[snippetId]/orchestrate/page.tsx b/web/app/(commonLayout)/snippets/[snippetId]/orchestrate/page.tsx
index 702dfcab54..8a39dc710b 100644
--- a/web/app/(commonLayout)/snippets/[snippetId]/orchestrate/page.tsx
+++ b/web/app/(commonLayout)/snippets/[snippetId]/orchestrate/page.tsx
@@ -5,7 +5,7 @@ const Page = async (props: {
}) => {
const { snippetId } = await props.params
- return
+ return
}
export default Page
diff --git a/web/app/components/snippets/__tests__/index.spec.tsx b/web/app/components/snippets/__tests__/index.spec.tsx
index 041783b987..8d63394a82 100644
--- a/web/app/components/snippets/__tests__/index.spec.tsx
+++ b/web/app/components/snippets/__tests__/index.spec.tsx
@@ -1,44 +1,16 @@
-import type { HeaderProps } from '@/app/components/workflow/header'
import type { SnippetDetailPayload } from '@/models/snippet'
-import { fireEvent, render, screen } from '@testing-library/react'
-import { PipelineInputVarType } from '@/models/pipeline'
+import { render, screen } from '@testing-library/react'
import SnippetPage from '..'
-import { useSnippetDetailStore } from '../store'
const mockUseSnippetInit = vi.fn()
-const mockPublishSnippetMutateAsync = vi.fn()
+const mockSetAppSidebarExpand = vi.fn()
vi.mock('../hooks/use-snippet-init', () => ({
useSnippetInit: (snippetId: string) => mockUseSnippetInit(snippetId),
}))
-vi.mock('../hooks/use-configs-map', () => ({
- useConfigsMap: () => ({
- flowId: 'snippet-1',
- flowType: 'snippet',
- fileSettings: {},
- }),
-}))
-
-vi.mock('../hooks/use-nodes-sync-draft', () => ({
- useNodesSyncDraft: () => ({
- doSyncWorkflowDraft: vi.fn(),
- syncInputFieldsDraft: vi.fn(),
- syncWorkflowDraftWhenPageClose: vi.fn(),
- }),
-}))
-
-vi.mock('../hooks/use-snippet-refresh-draft', () => ({
- useSnippetRefreshDraft: () => ({
- handleRefreshWorkflowDraft: vi.fn(),
- }),
-}))
-
-vi.mock('@/service/use-snippet-workflows', () => ({
- usePublishSnippetWorkflowMutation: () => ({
- mutateAsync: mockPublishSnippetMutateAsync,
- isPending: false,
- }),
+vi.mock('../components/snippet-main', () => ({
+ default: ({ snippetId }: { snippetId: string }) =>
{snippetId}
,
}))
vi.mock('@/next/navigation', () => ({
@@ -48,41 +20,14 @@ vi.mock('@/next/navigation', () => ({
}),
}))
-vi.mock('@/service/use-snippets', async (importOriginal) => {
- const actual = await importOriginal()
-
- return {
- ...actual,
- useUpdateSnippetMutation: () => ({
- mutate: vi.fn(),
- isPending: false,
- }),
- useExportSnippetMutation: () => ({
- mutateAsync: vi.fn(),
- isPending: false,
- }),
- useDeleteSnippetMutation: () => ({
- mutate: vi.fn(),
- isPending: false,
- }),
- }
-})
-
-vi.mock('@/service/use-common', () => ({
- useFileUploadConfig: () => ({
- data: undefined,
- }),
-}))
-
vi.mock('@/hooks/use-breakpoints', () => ({
default: () => 'desktop',
MediaType: { mobile: 'mobile', desktop: 'desktop' },
}))
-vi.mock('@/app/components/rag-pipeline/components/panel/input-field/hooks', () => ({
- useFloatingRight: () => ({
- floatingRight: false,
- floatingRightWidth: 400,
+vi.mock('@/app/components/app/store', () => ({
+ useStore: (selector: (state: { setAppSidebarExpand: typeof mockSetAppSidebarExpand }) => unknown) => selector({
+ setAppSidebarExpand: mockSetAppSidebarExpand,
}),
}))
@@ -90,26 +35,6 @@ vi.mock('@/app/components/workflow', () => ({
default: ({ children }: { children: React.ReactNode }) => (
{children}
),
- WorkflowWithInnerContext: ({ children, viewport }: { children: React.ReactNode, viewport?: { zoom?: number } }) => (
-
- {viewport?.zoom ?? 'none'}
- {children}
-
- ),
-}))
-
-vi.mock('@/app/components/workflow/header', () => ({
- default: (props: HeaderProps) => {
- const CustomRunMode = props.normal?.runAndHistoryProps?.components?.RunMode
-
- return (
-
- {props.normal?.components?.left}
- {CustomRunMode && }
- {props.normal?.components?.middle}
-
- )
- },
}))
vi.mock('@/app/components/workflow/context', () => ({
@@ -118,6 +43,16 @@ vi.mock('@/app/components/workflow/context', () => ({
),
}))
+vi.mock('@/app/components/workflow/utils', async (importOriginal) => {
+ const actual = await importOriginal()
+
+ return {
+ ...actual,
+ initialNodes: (nodes: unknown[]) => nodes,
+ initialEdges: (edges: unknown[]) => edges,
+ }
+})
+
vi.mock('@/app/components/app-sidebar', () => ({
default: ({
renderHeader,
@@ -139,27 +74,12 @@ vi.mock('@/app/components/app-sidebar/nav-link', () => ({
),
}))
-vi.mock('@/app/components/workflow/panel', () => ({
- default: ({ components }: { components?: { left?: React.ReactNode, right?: React.ReactNode } }) => (
-
-
{components?.left}
-
{components?.right}
-
- ),
+vi.mock('@/app/components/app-sidebar/snippet-info', () => ({
+ default: () => ,
}))
-vi.mock('@/app/components/workflow/utils', async (importOriginal) => {
- const actual = await importOriginal()
-
- return {
- ...actual,
- initialNodes: (nodes: unknown[]) => nodes,
- initialEdges: (edges: unknown[]) => edges,
- }
-})
-
-vi.mock('react-sortablejs', () => ({
- ReactSortable: ({ children }: { children: React.ReactNode }) => {children}
,
+vi.mock('@/app/components/evaluation', () => ({
+ default: ({ resourceId }: { resourceId: string }) => {resourceId}
,
}))
const mockSnippetDetail: SnippetDetailPayload = {
@@ -179,20 +99,10 @@ const mockSnippetDetail: SnippetDetailPayload = {
nodes: [],
edges: [],
},
- inputFields: [
- {
- type: PipelineInputVarType.textInput,
- label: 'Blog URL',
- variable: 'blog_url',
- required: true,
- options: [],
- placeholder: 'Paste a source article URL',
- max_length: 256,
- },
- ],
+ inputFields: [],
uiMeta: {
- inputFieldCount: 1,
- checklistCount: 2,
+ inputFieldCount: 0,
+ checklistCount: 0,
autoSavedAt: 'Auto-saved · a few seconds ago',
},
}
@@ -200,55 +110,23 @@ const mockSnippetDetail: SnippetDetailPayload = {
describe('SnippetPage', () => {
beforeEach(() => {
vi.clearAllMocks()
- useSnippetDetailStore.getState().reset()
- mockPublishSnippetMutateAsync.mockResolvedValue(undefined)
mockUseSnippetInit.mockReturnValue({
data: mockSnippetDetail,
isLoading: false,
})
})
- it('should render the snippet detail shell', () => {
+ it('should render the orchestrate route shell with independent main content', () => {
render()
- expect(screen.getByText('Tone Rewriter')).toBeInTheDocument()
- expect(screen.getByText('A static snippet mock.')).toBeInTheDocument()
expect(screen.getByTestId('app-sidebar')).toBeInTheDocument()
+ expect(screen.getByTestId('snippet-info')).toBeInTheDocument()
expect(screen.getByTestId('workflow-context-provider')).toBeInTheDocument()
expect(screen.getByTestId('workflow-default-context')).toBeInTheDocument()
- expect(screen.getByTestId('workflow-inner-context')).toBeInTheDocument()
- expect(screen.getByTestId('workflow-viewport-zoom').textContent).toBe('1')
+ expect(screen.getByTestId('snippet-main')).toHaveTextContent('snippet-1')
})
- it('should open the input field panel and editor', () => {
- render()
-
- fireEvent.click(screen.getAllByRole('button', { name: /snippet\.inputFieldButton/i })[0])
- expect(screen.getAllByText('snippet.panelTitle').length).toBeGreaterThan(0)
-
- fireEvent.click(screen.getAllByRole('button', { name: /datasetPipeline\.inputFieldPanel\.addInputField/i })[0])
- expect(screen.getAllByText('datasetPipeline.inputFieldPanel.addInputField').length).toBeGreaterThan(1)
- })
-
- it('should toggle the publish menu', () => {
- render()
-
- fireEvent.click(screen.getByRole('button', { name: /snippet\.publishButton/i }))
- expect(screen.getByText('snippet.publishMenuCurrentDraft')).toBeInTheDocument()
- })
-
- it('should publish the snippet when clicking publish in the menu', async () => {
- render()
-
- fireEvent.click(screen.getByRole('button', { name: /snippet\.publishButton/i }))
- fireEvent.click(screen.getAllByRole('button', { name: /snippet\.publishButton/i })[1])
-
- expect(mockPublishSnippetMutateAsync).toHaveBeenCalledWith({
- params: { snippetId: 'snippet-1' },
- })
- })
-
- it('should render loading fallback when snippet data is unavailable', () => {
+ it('should render loading fallback when orchestrate data is unavailable', () => {
mockUseSnippetInit.mockReturnValue({
data: null,
isLoading: false,
diff --git a/web/app/components/snippets/__tests__/snippet-evaluation-page.spec.tsx b/web/app/components/snippets/__tests__/snippet-evaluation-page.spec.tsx
new file mode 100644
index 0000000000..21595ac963
--- /dev/null
+++ b/web/app/components/snippets/__tests__/snippet-evaluation-page.spec.tsx
@@ -0,0 +1,107 @@
+import type { Snippet } from '@/types/snippet'
+import { render, screen } from '@testing-library/react'
+import SnippetEvaluationPage from '../snippet-evaluation-page'
+
+const mockUseSnippetApiDetail = vi.fn()
+const mockSetAppSidebarExpand = vi.fn()
+
+vi.mock('@/service/use-snippets', async (importOriginal) => {
+ const actual = await importOriginal()
+
+ return {
+ ...actual,
+ useSnippetApiDetail: (snippetId: string) => mockUseSnippetApiDetail(snippetId),
+ useUpdateSnippetMutation: () => ({
+ mutate: vi.fn(),
+ isPending: false,
+ }),
+ useExportSnippetMutation: () => ({
+ mutateAsync: vi.fn(),
+ isPending: false,
+ }),
+ useDeleteSnippetMutation: () => ({
+ mutate: vi.fn(),
+ isPending: false,
+ }),
+ }
+})
+
+vi.mock('@/next/navigation', () => ({
+ useRouter: () => ({
+ replace: vi.fn(),
+ push: vi.fn(),
+ }),
+}))
+
+vi.mock('@/hooks/use-breakpoints', () => ({
+ default: () => 'desktop',
+ MediaType: { mobile: 'mobile', desktop: 'desktop' },
+}))
+
+vi.mock('@/app/components/app/store', () => ({
+ useStore: (selector: (state: { setAppSidebarExpand: typeof mockSetAppSidebarExpand }) => unknown) => selector({
+ setAppSidebarExpand: mockSetAppSidebarExpand,
+ }),
+}))
+
+vi.mock('@/app/components/app-sidebar', () => ({
+ default: ({
+ renderHeader,
+ renderNavigation,
+ }: {
+ renderHeader?: (modeState: string) => React.ReactNode
+ renderNavigation?: (modeState: string) => React.ReactNode
+ }) => (
+
+
{renderHeader?.('expand')}
+
{renderNavigation?.('expand')}
+
+ ),
+}))
+
+vi.mock('@/app/components/app-sidebar/nav-link', () => ({
+ default: ({ name, onClick }: { name: string, onClick?: () => void }) => (
+
+ ),
+}))
+
+vi.mock('@/app/components/evaluation', () => ({
+ default: ({ resourceId }: { resourceId: string }) => {resourceId}
,
+}))
+
+const mockSnippetApiDetail: Snippet = {
+ id: 'snippet-1',
+ name: 'Tone Rewriter',
+ description: 'A static snippet mock.',
+ type: 'node',
+ is_published: false,
+ version: 'draft',
+ use_count: 19,
+ icon_info: {
+ icon_type: 'emoji',
+ icon: '🪄',
+ icon_background: '#E0EAFF',
+ },
+ input_fields: [],
+ created_at: 1_711_609_600,
+ updated_at: 1_711_616_800,
+ author: 'Evan',
+}
+
+describe('SnippetEvaluationPage', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ mockUseSnippetApiDetail.mockReturnValue({
+ data: mockSnippetApiDetail,
+ isLoading: false,
+ })
+ })
+
+ it('should fetch evaluation route data independently from snippet init', () => {
+ render()
+
+ expect(mockUseSnippetApiDetail).toHaveBeenCalledWith('snippet-1')
+ expect(screen.getByTestId('app-sidebar')).toBeInTheDocument()
+ expect(screen.getByTestId('evaluation')).toHaveTextContent('snippet-1')
+ })
+})
diff --git a/web/app/components/snippets/components/__tests__/snippet-main.spec.tsx b/web/app/components/snippets/components/__tests__/snippet-main.spec.tsx
index ff3a3961ef..c6a6b5a231 100644
--- a/web/app/components/snippets/components/__tests__/snippet-main.spec.tsx
+++ b/web/app/components/snippets/components/__tests__/snippet-main.spec.tsx
@@ -1,10 +1,9 @@
import type { WorkflowProps } from '@/app/components/workflow'
-import type { SnippetDetailPayload, SnippetInputField, SnippetSection } from '@/models/snippet'
+import type { SnippetDetailPayload, SnippetInputField } from '@/models/snippet'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { PipelineInputVarType } from '@/models/pipeline'
import SnippetMain from '../snippet-main'
-const mockSetAppSidebarExpand = vi.fn()
const mockSyncInputFieldsDraft = vi.fn()
const mockCloseEditor = vi.fn()
const mockOpenEditor = vi.fn()
@@ -40,17 +39,6 @@ const mockInspectVarsCrud = {
}
let capturedHooksStore: Record | undefined
-vi.mock('@/hooks/use-breakpoints', () => ({
- default: () => 'desktop',
- MediaType: { mobile: 'mobile', desktop: 'desktop' },
-}))
-
-vi.mock('@/app/components/app/store', () => ({
- useStore: (selector: (state: { setAppSidebarExpand: typeof mockSetAppSidebarExpand }) => unknown) => selector({
- setAppSidebarExpand: mockSetAppSidebarExpand,
- }),
-}))
-
vi.mock('@/app/components/snippets/store', () => ({
useSnippetDetailStore: (selector: (state: {
editingField: SnippetInputField | null
@@ -135,33 +123,6 @@ vi.mock('@/app/components/snippets/hooks/use-snippet-start-run', () => ({
}),
}))
-vi.mock('@/app/components/app-sidebar', () => ({
- default: ({
- renderHeader,
- renderNavigation,
- }: {
- renderHeader?: (modeState: string) => React.ReactNode
- renderNavigation?: (modeState: string) => React.ReactNode
- }) => (
-
-
{renderHeader?.('expand')}
-
{renderNavigation?.('expand')}
-
- ),
-}))
-
-vi.mock('@/app/components/app-sidebar/nav-link', () => ({
- default: ({ name }: { name: string }) => {name}
,
-}))
-
-vi.mock('@/app/components/app-sidebar/snippet-info', () => ({
- default: () => ,
-}))
-
-vi.mock('@/app/components/evaluation', () => ({
- default: () => ,
-}))
-
vi.mock('@/app/components/workflow', () => ({
WorkflowWithInnerContext: ({
children,
@@ -237,12 +198,11 @@ const payload: SnippetDetailPayload = {
},
}
-const renderSnippetMain = (section: SnippetSection = 'orchestrate') => {
+const renderSnippetMain = () => {
return render(
{
const { t } = useTranslation('snippet')
const publishSnippetMutation = usePublishSnippetWorkflowMutation(snippetId)
@@ -41,7 +38,7 @@ export const useSnippetPublish = ({
}, [publishSnippetMutation, setPublishMenuOpen, snippetId, t])
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.shift.p`, (event) => {
- if (section !== 'orchestrate' || publishSnippetMutation.isPending)
+ if (publishSnippetMutation.isPending)
return
event.preventDefault()
diff --git a/web/app/components/snippets/components/snippet-layout.tsx b/web/app/components/snippets/components/snippet-layout.tsx
new file mode 100644
index 0000000000..37900f3969
--- /dev/null
+++ b/web/app/components/snippets/components/snippet-layout.tsx
@@ -0,0 +1,88 @@
+'use client'
+
+import type { ReactNode } from 'react'
+import type { NavIcon } from '@/app/components/app-sidebar/nav-link'
+import type { SnippetDetail, SnippetSection } from '@/models/snippet'
+import {
+ RiFlaskFill,
+ RiFlaskLine,
+ RiTerminalWindowFill,
+ RiTerminalWindowLine,
+} from '@remixicon/react'
+import { useEffect } from 'react'
+import { useTranslation } from 'react-i18next'
+import AppSideBar from '@/app/components/app-sidebar'
+import NavLink from '@/app/components/app-sidebar/nav-link'
+import SnippetInfo from '@/app/components/app-sidebar/snippet-info'
+import { useStore as useAppStore } from '@/app/components/app/store'
+import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
+
+type SnippetLayoutProps = {
+ children: ReactNode
+ section: SnippetSection
+ snippet: SnippetDetail
+ snippetId: string
+}
+
+const ORCHESTRATE_ICONS: { normal: NavIcon, selected: NavIcon } = {
+ normal: RiTerminalWindowLine,
+ selected: RiTerminalWindowFill,
+}
+
+const EVALUATION_ICONS: { normal: NavIcon, selected: NavIcon } = {
+ normal: RiFlaskLine,
+ selected: RiFlaskFill,
+}
+
+const SnippetLayout = ({
+ children,
+ section,
+ snippet,
+ snippetId,
+}: SnippetLayoutProps) => {
+ const { t } = useTranslation('snippet')
+ const media = useBreakpoints()
+ const isMobile = media === MediaType.mobile
+ const setAppSidebarExpand = useAppStore(state => state.setAppSidebarExpand)
+
+ useEffect(() => {
+ const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
+ const mode = isMobile ? 'collapse' : 'expand'
+ setAppSidebarExpand(isMobile ? mode : localeMode)
+ }, [isMobile, setAppSidebarExpand])
+
+ return (
+
+
}
+ renderNavigation={mode => (
+ <>
+
+
+ >
+ )}
+ />
+
+
+
+ )
+}
+
+export default SnippetLayout
diff --git a/web/app/components/snippets/components/snippet-main.tsx b/web/app/components/snippets/components/snippet-main.tsx
index 46740c344a..f65ec8c6f8 100644
--- a/web/app/components/snippets/components/snippet-main.tsx
+++ b/web/app/components/snippets/components/snippet-main.tsx
@@ -1,29 +1,15 @@
'use client'
-import type { NavIcon } from '@/app/components/app-sidebar/nav-link'
import type { WorkflowProps } from '@/app/components/workflow'
-import type { SnippetDetailPayload, SnippetSection } from '@/models/snippet'
-import {
- RiFlaskFill,
- RiFlaskLine,
- RiTerminalWindowFill,
- RiTerminalWindowLine,
-} from '@remixicon/react'
+import type { SnippetDetailPayload } from '@/models/snippet'
import {
useEffect,
useMemo,
} from 'react'
-import { useTranslation } from 'react-i18next'
-import AppSideBar from '@/app/components/app-sidebar'
-import NavLink from '@/app/components/app-sidebar/nav-link'
-import SnippetInfo from '@/app/components/app-sidebar/snippet-info'
-import { useStore as useAppStore } from '@/app/components/app/store'
-import Evaluation from '@/app/components/evaluation'
import { WorkflowWithInnerContext } from '@/app/components/workflow'
import { useAvailableNodesMetaData } from '@/app/components/workflow-app/hooks'
import { useSetWorkflowVarsWithValue } from '@/app/components/workflow/hooks/use-fetch-workflow-inspect-vars'
import { BlockEnum } from '@/app/components/workflow/types'
-import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { useConfigsMap } from '../hooks/use-configs-map'
import { useInspectVarsCrud } from '../hooks/use-inspect-vars-crud'
import { useNodesSyncDraft } from '../hooks/use-nodes-sync-draft'
@@ -38,31 +24,16 @@ import SnippetChildren from './snippet-children'
type SnippetMainProps = {
payload: SnippetDetailPayload
snippetId: string
- section: SnippetSection
} & Pick
-const ORCHESTRATE_ICONS: { normal: NavIcon, selected: NavIcon } = {
- normal: RiTerminalWindowLine,
- selected: RiTerminalWindowFill,
-}
-
-const EVALUATION_ICONS: { normal: NavIcon, selected: NavIcon } = {
- normal: RiFlaskLine,
- selected: RiFlaskFill,
-}
-
const SnippetMain = ({
payload,
snippetId,
- section,
nodes,
edges,
viewport,
}: SnippetMainProps) => {
- const { t } = useTranslation('snippet')
- const { graph, snippet, uiMeta } = payload
- const media = useBreakpoints()
- const isMobile = media === MediaType.mobile
+ const { graph, uiMeta } = payload
const {
doSyncWorkflowDraft,
syncWorkflowDraftWhenPageClose,
@@ -114,7 +85,6 @@ const SnippetMain = ({
nodesMap,
}
}, [workflowAvailableNodesMetaData])
- const setAppSidebarExpand = useAppStore(state => state.setAppSidebarExpand)
const reset = useSnippetDetailStore(state => state.reset)
const {
editingField,
@@ -139,7 +109,6 @@ const SnippetMain = ({
setPublishMenuOpen,
} = useSnippetPublish({
snippetId,
- section,
})
const {
handleStartWorkflowRun,
@@ -153,12 +122,6 @@ const SnippetMain = ({
reset()
}, [reset, snippetId])
- useEffect(() => {
- const localeMode = localStorage.getItem('app-detail-collapse-or-expand') || 'expand'
- const mode = isMobile ? 'collapse' : 'expand'
- setAppSidebarExpand(isMobile ? mode : localeMode)
- }, [isMobile, setAppSidebarExpand])
-
const hooksStore = useMemo(() => {
return {
doSyncWorkflowDraft,
@@ -220,67 +183,32 @@ const SnippetMain = ({
])
return (
-
-
}
- renderNavigation={mode => (
- <>
-
-
- >
- )}
+
+
-
-
-
- {section === 'evaluation'
- ? (
-
- )
- : (
-
-
-
- )}
-
-
-
+
)
}
diff --git a/web/app/components/snippets/index.tsx b/web/app/components/snippets/index.tsx
index 706fdf7a27..6d3bcb570e 100644
--- a/web/app/components/snippets/index.tsx
+++ b/web/app/components/snippets/index.tsx
@@ -1,6 +1,5 @@
'use client'
-import type { SnippetSection } from '@/models/snippet'
import { useMemo } from 'react'
import Loading from '@/app/components/base/loading'
import WorkflowWithDefaultContext from '@/app/components/workflow'
@@ -9,18 +8,23 @@ import {
initialEdges,
initialNodes,
} from '@/app/components/workflow/utils'
+import SnippetLayout from './components/snippet-layout'
import SnippetMain from './components/snippet-main'
import { useSnippetInit } from './hooks/use-snippet-init'
type SnippetPageProps = {
snippetId: string
- section?: SnippetSection
}
-const SnippetPage = ({
- snippetId,
- section = 'orchestrate',
-}: SnippetPageProps) => {
+const SnippetPageLoading = () => {
+ return (
+
+
+
+ )
+}
+
+const SnippetPage = ({ snippetId }: SnippetPageProps) => {
const { data, isLoading } = useSnippetInit(snippetId)
const nodesData = useMemo(() => {
if (!data)
@@ -36,35 +40,36 @@ const SnippetPage = ({
}, [data])
if (!data || isLoading) {
- return (
-
-
-
- )
+ return
}
return (
-
-
-
+ nodes={nodesData}
+ >
+
+
+
)
}
-const SnippetPageWrapper = (props: SnippetPageProps) => {
+const SnippetPageWrapper = ({ snippetId }: SnippetPageProps) => {
return (
-
+
)
}
diff --git a/web/app/components/snippets/snippet-evaluation-page.tsx b/web/app/components/snippets/snippet-evaluation-page.tsx
new file mode 100644
index 0000000000..e52c857b3d
--- /dev/null
+++ b/web/app/components/snippets/snippet-evaluation-page.tsx
@@ -0,0 +1,46 @@
+'use client'
+
+import { useMemo } from 'react'
+import Loading from '@/app/components/base/loading'
+import Evaluation from '@/app/components/evaluation'
+import { buildSnippetDetailPayload, useSnippetApiDetail } from '@/service/use-snippets'
+import { getSnippetDetailMock } from '@/service/use-snippets.mock'
+import SnippetLayout from './components/snippet-layout'
+
+type SnippetEvaluationPageProps = {
+ snippetId: string
+}
+
+const SnippetEvaluationPage = ({ snippetId }: SnippetEvaluationPageProps) => {
+ const snippetApiDetail = useSnippetApiDetail(snippetId)
+ const mockSnippet = useMemo(() => getSnippetDetailMock(snippetId)?.snippet, [snippetId])
+ const snippet = useMemo(() => {
+ if (snippetApiDetail.data)
+ return buildSnippetDetailPayload(snippetApiDetail.data).snippet
+
+ if (!snippetApiDetail.isLoading)
+ return mockSnippet
+
+ return undefined
+ }, [mockSnippet, snippetApiDetail.data, snippetApiDetail.isLoading])
+
+ if (!snippet || snippetApiDetail.isLoading) {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+
+ )
+}
+
+export default SnippetEvaluationPage