mirror of
https://github.com/langgenius/dify.git
synced 2026-05-07 02:46:32 +08:00
feat(web): snippet publish
This commit is contained in:
parent
a4ea33167d
commit
0ad268aa7d
@ -6,6 +6,7 @@ import SnippetPage from '..'
|
||||
import { useSnippetDetailStore } from '../store'
|
||||
|
||||
const mockUseSnippetInit = vi.fn()
|
||||
const mockPublishSnippetMutateAsync = vi.fn()
|
||||
|
||||
vi.mock('../hooks/use-snippet-init', () => ({
|
||||
useSnippetInit: (snippetId: string) => mockUseSnippetInit(snippetId),
|
||||
@ -33,6 +34,13 @@ vi.mock('../hooks/use-snippet-refresh-draft', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-snippet-workflows', () => ({
|
||||
usePublishSnippetWorkflowMutation: () => ({
|
||||
mutateAsync: mockPublishSnippetMutateAsync,
|
||||
isPending: false,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/next/navigation', () => ({
|
||||
useRouter: () => ({
|
||||
replace: vi.fn(),
|
||||
@ -186,6 +194,7 @@ describe('SnippetPage', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
useSnippetDetailStore.getState().reset()
|
||||
mockPublishSnippetMutateAsync.mockResolvedValue(undefined)
|
||||
mockUseSnippetInit.mockReturnValue({
|
||||
data: mockSnippetDetail,
|
||||
isLoading: false,
|
||||
@ -221,6 +230,17 @@ describe('SnippetPage', () => {
|
||||
expect(screen.getByText('snippet.publishMenuCurrentDraft')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should publish the snippet when clicking publish in the menu', async () => {
|
||||
render(<SnippetPage snippetId="snippet-1" />)
|
||||
|
||||
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', () => {
|
||||
mockUseSnippetInit.mockReturnValue({
|
||||
data: null,
|
||||
|
||||
@ -0,0 +1,22 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import PublishMenu from '../publish-menu'
|
||||
|
||||
describe('PublishMenu', () => {
|
||||
it('should render the draft summary and publish shortcut', () => {
|
||||
const { container } = render(
|
||||
<PublishMenu
|
||||
uiMeta={{
|
||||
inputFieldCount: 1,
|
||||
checklistCount: 2,
|
||||
autoSavedAt: 'Auto-saved · a few seconds ago',
|
||||
}}
|
||||
onPublish={vi.fn()}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('snippet.publishMenuCurrentDraft')).toBeInTheDocument()
|
||||
expect(screen.getByText('Auto-saved · a few seconds ago')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'snippet.publishButton' })).toBeInTheDocument()
|
||||
expect(container.querySelectorAll('.system-kbd')).toHaveLength(3)
|
||||
})
|
||||
})
|
||||
@ -10,8 +10,10 @@ const mockCloseEditor = vi.fn()
|
||||
const mockOpenEditor = vi.fn()
|
||||
const mockReset = vi.fn()
|
||||
const mockSetInputPanelOpen = vi.fn()
|
||||
const mockSetPublishMenuOpen = vi.fn()
|
||||
const mockToggleInputPanel = vi.fn()
|
||||
const mockTogglePublishMenu = vi.fn()
|
||||
const mockPublishSnippetMutateAsync = vi.fn()
|
||||
|
||||
vi.mock('@/hooks/use-breakpoints', () => ({
|
||||
default: () => 'desktop',
|
||||
@ -34,6 +36,7 @@ vi.mock('@/app/components/snippets/store', () => ({
|
||||
openEditor: typeof mockOpenEditor
|
||||
reset: typeof mockReset
|
||||
setInputPanelOpen: typeof mockSetInputPanelOpen
|
||||
setPublishMenuOpen: typeof mockSetPublishMenuOpen
|
||||
toggleInputPanel: typeof mockToggleInputPanel
|
||||
togglePublishMenu: typeof mockTogglePublishMenu
|
||||
}) => unknown) => selector({
|
||||
@ -45,11 +48,19 @@ vi.mock('@/app/components/snippets/store', () => ({
|
||||
openEditor: mockOpenEditor,
|
||||
reset: mockReset,
|
||||
setInputPanelOpen: mockSetInputPanelOpen,
|
||||
setPublishMenuOpen: mockSetPublishMenuOpen,
|
||||
toggleInputPanel: mockToggleInputPanel,
|
||||
togglePublishMenu: mockTogglePublishMenu,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/service/use-snippet-workflows', () => ({
|
||||
usePublishSnippetWorkflowMutation: () => ({
|
||||
mutateAsync: mockPublishSnippetMutateAsync,
|
||||
isPending: false,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/snippets/hooks/use-configs-map', () => ({
|
||||
useConfigsMap: () => ({
|
||||
flowId: 'snippet-1',
|
||||
@ -108,13 +119,16 @@ vi.mock('@/app/components/workflow', () => ({
|
||||
vi.mock('@/app/components/snippets/components/snippet-children', () => ({
|
||||
default: ({
|
||||
onRemoveField,
|
||||
onPublish,
|
||||
onSubmitField,
|
||||
}: {
|
||||
onRemoveField: (index: number) => void
|
||||
onPublish: () => void
|
||||
onSubmitField: (field: SnippetInputField) => void
|
||||
}) => (
|
||||
<div>
|
||||
<button type="button" onClick={() => onRemoveField(0)}>remove</button>
|
||||
<button type="button" onClick={onPublish}>publish</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onSubmitField({
|
||||
@ -178,6 +192,7 @@ describe('SnippetMain', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockSyncInputFieldsDraft.mockResolvedValue(undefined)
|
||||
mockPublishSnippetMutateAsync.mockResolvedValue(undefined)
|
||||
})
|
||||
|
||||
describe('Input Fields Sync', () => {
|
||||
@ -213,4 +228,19 @@ describe('SnippetMain', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Publish', () => {
|
||||
it('should call the publish mutation and close the publish menu', async () => {
|
||||
renderSnippetMain()
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'publish' }))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockPublishSnippetMutateAsync).toHaveBeenCalledWith({
|
||||
params: { snippetId: 'snippet-1' },
|
||||
})
|
||||
})
|
||||
expect(mockSetPublishMenuOpen).toHaveBeenCalledWith(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -3,24 +3,43 @@
|
||||
import type { SnippetDetailUIModel } from '@/models/snippet'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import ShortcutsName from '@/app/components/workflow/shortcuts-name'
|
||||
|
||||
const PublishMenu = ({
|
||||
uiMeta,
|
||||
onPublish,
|
||||
isPublishing = false,
|
||||
}: {
|
||||
uiMeta: SnippetDetailUIModel
|
||||
onPublish: () => void
|
||||
isPublishing?: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation('snippet')
|
||||
|
||||
return (
|
||||
<div className="w-80 rounded-2xl border border-components-panel-border bg-components-panel-bg p-4 shadow-[0px_20px_24px_-4px_rgba(9,9,11,0.08),0px_8px_8px_-4px_rgba(9,9,11,0.03)]">
|
||||
<div className="text-text-tertiary system-xs-semibold-uppercase">
|
||||
{t('publishMenuCurrentDraft')}
|
||||
<div className="flex flex-col gap-3 px-4 pb-4 pt-3">
|
||||
<div className="flex flex-col">
|
||||
<div className="min-h-6 text-text-tertiary system-xs-medium-uppercase">
|
||||
{t('publishMenuCurrentDraft')}
|
||||
</div>
|
||||
<div className="text-text-secondary system-sm-medium">
|
||||
{uiMeta.autoSavedAt}
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-1 text-text-secondary system-sm-medium">
|
||||
{uiMeta.autoSavedAt}
|
||||
</div>
|
||||
<Button variant="primary" size="small" className="mt-4 w-full justify-center">
|
||||
{t('publishButton')}
|
||||
<Button
|
||||
variant="primary"
|
||||
loading={isPublishing}
|
||||
disabled={isPublishing}
|
||||
className="w-full justify-center gap-1.5"
|
||||
onClick={onPublish}
|
||||
>
|
||||
<span>{t('publishButton')}</span>
|
||||
<div aria-hidden="true">
|
||||
<ShortcutsName
|
||||
keys={['ctrl', 'shift', 'p']}
|
||||
bgColor="white"
|
||||
/>
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
import type { SnippetDetailUIModel, SnippetInputField } from '@/models/snippet'
|
||||
import SnippetInputFieldEditor from './input-field-editor'
|
||||
import SnippetInputFieldPanel from './panel'
|
||||
import PublishMenu from './publish-menu'
|
||||
import SnippetHeader from './snippet-header'
|
||||
import SnippetWorkflowPanel from './workflow-panel'
|
||||
|
||||
@ -15,9 +14,11 @@ type SnippetChildrenProps = {
|
||||
isEditorOpen: boolean
|
||||
isInputPanelOpen: boolean
|
||||
isPublishMenuOpen: boolean
|
||||
isPublishing: boolean
|
||||
onToggleInputPanel: () => void
|
||||
onTogglePublishMenu: () => void
|
||||
onPublishMenuOpenChange: (open: boolean) => void
|
||||
onCloseInputPanel: () => void
|
||||
onPublish: () => void
|
||||
onOpenEditor: (field?: SnippetInputField | null) => void
|
||||
onCloseEditor: () => void
|
||||
onSubmitField: (field: SnippetInputField) => void
|
||||
@ -33,9 +34,11 @@ const SnippetChildren = ({
|
||||
isEditorOpen,
|
||||
isInputPanelOpen,
|
||||
isPublishMenuOpen,
|
||||
isPublishing,
|
||||
onToggleInputPanel,
|
||||
onTogglePublishMenu,
|
||||
onPublishMenuOpenChange,
|
||||
onCloseInputPanel,
|
||||
onPublish,
|
||||
onOpenEditor,
|
||||
onCloseEditor,
|
||||
onSubmitField,
|
||||
@ -49,8 +52,12 @@ const SnippetChildren = ({
|
||||
<SnippetHeader
|
||||
snippetId={snippetId}
|
||||
inputFieldCount={fields.length}
|
||||
uiMeta={uiMeta}
|
||||
isPublishMenuOpen={isPublishMenuOpen}
|
||||
isPublishing={isPublishing}
|
||||
onToggleInputPanel={onToggleInputPanel}
|
||||
onTogglePublishMenu={onTogglePublishMenu}
|
||||
onPublishMenuOpenChange={onPublishMenuOpenChange}
|
||||
onPublish={onPublish}
|
||||
/>
|
||||
|
||||
<SnippetWorkflowPanel
|
||||
@ -67,12 +74,6 @@ const SnippetChildren = ({
|
||||
onSortChange={onSortChange}
|
||||
/>
|
||||
|
||||
{isPublishMenuOpen && (
|
||||
<div className="absolute right-3 top-14 z-20">
|
||||
<PublishMenu uiMeta={uiMeta} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isInputPanelOpen && (
|
||||
<div className="pointer-events-none absolute inset-y-3 right-3 z-30 flex justify-end">
|
||||
<div className="pointer-events-auto h-full xl:hidden">
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { HeaderProps } from '@/app/components/workflow/header'
|
||||
import type { SnippetDetailUIModel } from '@/models/snippet'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import SnippetHeader from '..'
|
||||
|
||||
@ -23,7 +24,13 @@ vi.mock('@/app/components/workflow/header', () => ({
|
||||
|
||||
describe('SnippetHeader', () => {
|
||||
const mockToggleInputPanel = vi.fn()
|
||||
const mockTogglePublishMenu = vi.fn()
|
||||
const mockPublishMenuOpenChange = vi.fn()
|
||||
const mockPublish = vi.fn()
|
||||
const uiMeta: SnippetDetailUIModel = {
|
||||
inputFieldCount: 1,
|
||||
checklistCount: 2,
|
||||
autoSavedAt: 'Auto-saved · a few seconds ago',
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
@ -36,8 +43,12 @@ describe('SnippetHeader', () => {
|
||||
<SnippetHeader
|
||||
snippetId="snippet-1"
|
||||
inputFieldCount={3}
|
||||
uiMeta={uiMeta}
|
||||
isPublishMenuOpen={false}
|
||||
isPublishing={false}
|
||||
onToggleInputPanel={mockToggleInputPanel}
|
||||
onTogglePublishMenu={mockTogglePublishMenu}
|
||||
onPublishMenuOpenChange={mockPublishMenuOpenChange}
|
||||
onPublish={mockPublish}
|
||||
/>,
|
||||
)
|
||||
|
||||
@ -53,13 +64,17 @@ describe('SnippetHeader', () => {
|
||||
|
||||
// Verifies forwarded callbacks still drive the snippet-specific controls.
|
||||
describe('User Interactions', () => {
|
||||
it('should invoke the snippet callbacks when input and publish buttons are clicked', () => {
|
||||
it('should invoke the snippet callbacks when input and publish trigger are clicked', () => {
|
||||
render(
|
||||
<SnippetHeader
|
||||
snippetId="snippet-1"
|
||||
inputFieldCount={1}
|
||||
uiMeta={uiMeta}
|
||||
isPublishMenuOpen={false}
|
||||
isPublishing={false}
|
||||
onToggleInputPanel={mockToggleInputPanel}
|
||||
onTogglePublishMenu={mockTogglePublishMenu}
|
||||
onPublishMenuOpenChange={mockPublishMenuOpenChange}
|
||||
onPublish={mockPublish}
|
||||
/>,
|
||||
)
|
||||
|
||||
@ -67,7 +82,8 @@ describe('SnippetHeader', () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: /snippet\.publishButton/i }))
|
||||
|
||||
expect(mockToggleInputPanel).toHaveBeenCalledTimes(1)
|
||||
expect(mockTogglePublishMenu).toHaveBeenCalledTimes(1)
|
||||
expect(mockPublishMenuOpenChange).toHaveBeenCalledTimes(1)
|
||||
expect(mockPublishMenuOpenChange.mock.calls[0][0]).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { HeaderProps } from '@/app/components/workflow/header'
|
||||
import type { SnippetDetailUIModel } from '@/models/snippet'
|
||||
import {
|
||||
memo,
|
||||
useMemo,
|
||||
@ -13,15 +14,23 @@ import RunMode from './run-mode'
|
||||
type SnippetHeaderProps = {
|
||||
snippetId: string
|
||||
inputFieldCount: number
|
||||
uiMeta: SnippetDetailUIModel
|
||||
isPublishMenuOpen: boolean
|
||||
isPublishing: boolean
|
||||
onToggleInputPanel: () => void
|
||||
onTogglePublishMenu: () => void
|
||||
onPublishMenuOpenChange: (open: boolean) => void
|
||||
onPublish: () => void
|
||||
}
|
||||
|
||||
const SnippetHeader = ({
|
||||
snippetId,
|
||||
inputFieldCount,
|
||||
uiMeta,
|
||||
isPublishMenuOpen,
|
||||
isPublishing,
|
||||
onToggleInputPanel,
|
||||
onTogglePublishMenu,
|
||||
onPublishMenuOpenChange,
|
||||
onPublish,
|
||||
}: SnippetHeaderProps) => {
|
||||
const viewHistoryProps = useMemo(() => {
|
||||
return {
|
||||
@ -34,7 +43,15 @@ const SnippetHeader = ({
|
||||
normal: {
|
||||
components: {
|
||||
left: <InputFieldButton count={inputFieldCount} onClick={onToggleInputPanel} />,
|
||||
middle: <Publisher onClick={onTogglePublishMenu} />,
|
||||
middle: (
|
||||
<Publisher
|
||||
uiMeta={uiMeta}
|
||||
open={isPublishMenuOpen}
|
||||
isPublishing={isPublishing}
|
||||
onOpenChange={onPublishMenuOpenChange}
|
||||
onPublish={onPublish}
|
||||
/>
|
||||
),
|
||||
},
|
||||
controls: {
|
||||
showEnvButton: false,
|
||||
@ -52,7 +69,7 @@ const SnippetHeader = ({
|
||||
viewHistoryProps,
|
||||
},
|
||||
}
|
||||
}, [inputFieldCount, onToggleInputPanel, onTogglePublishMenu, viewHistoryProps])
|
||||
}, [inputFieldCount, isPublishMenuOpen, isPublishing, onPublish, onPublishMenuOpenChange, onToggleInputPanel, uiMeta, viewHistoryProps])
|
||||
|
||||
return <Header {...headerProps} />
|
||||
}
|
||||
|
||||
@ -1,26 +1,50 @@
|
||||
'use client'
|
||||
|
||||
import type { SnippetDetailUIModel } from '@/models/snippet'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/app/components/base/ui/dropdown-menu'
|
||||
import PublishMenu from '../publish-menu'
|
||||
|
||||
type PublisherProps = {
|
||||
onClick: () => void
|
||||
uiMeta: SnippetDetailUIModel
|
||||
open: boolean
|
||||
isPublishing: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
onPublish: () => void
|
||||
}
|
||||
|
||||
const Publisher = ({
|
||||
onClick,
|
||||
uiMeta,
|
||||
open,
|
||||
isPublishing,
|
||||
onOpenChange,
|
||||
onPublish,
|
||||
}: PublisherProps) => {
|
||||
const { t } = useTranslation('snippet')
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-1 rounded-lg bg-components-button-primary-bg px-3 py-2 text-white shadow-[0px_2px_2px_-1px_rgba(0,0,0,0.12),0px_1px_1px_-1px_rgba(0,0,0,0.12),0px_0px_0px_0.5px_rgba(9,9,11,0.05)]"
|
||||
onClick={onClick}
|
||||
>
|
||||
<span className="text-[13px] font-medium leading-4">{t('publishButton')}</span>
|
||||
<span aria-hidden className="i-ri-arrow-down-s-line h-4 w-4" />
|
||||
</button>
|
||||
<DropdownMenu open={open} onOpenChange={onOpenChange}>
|
||||
<DropdownMenuTrigger className="flex items-center gap-1 rounded-lg bg-components-button-primary-bg px-3 py-2 text-white shadow-[0px_2px_2px_-1px_rgba(0,0,0,0.12),0px_1px_1px_-1px_rgba(0,0,0,0.12),0px_0px_0px_0.5px_rgba(9,9,11,0.05)]">
|
||||
<span className="text-[13px] font-medium leading-4">{t('publishButton')}</span>
|
||||
<span aria-hidden className="i-ri-arrow-down-s-line h-4 w-4" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
placement="bottom-end"
|
||||
sideOffset={6}
|
||||
popupClassName="w-80 !rounded-2xl !bg-components-panel-bg !p-0 !shadow-[0px_20px_24px_-4px_rgba(9,9,11,0.08),0px_8px_8px_-4px_rgba(9,9,11,0.03)]"
|
||||
>
|
||||
<PublishMenu
|
||||
uiMeta={uiMeta}
|
||||
isPublishing={isPublishing}
|
||||
onPublish={onPublish}
|
||||
/>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,10 @@ import {
|
||||
RiTerminalWindowLine,
|
||||
} from '@remixicon/react'
|
||||
import {
|
||||
useKeyPress,
|
||||
} from 'ahooks'
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
@ -25,7 +29,9 @@ import Evaluation from '@/app/components/evaluation'
|
||||
import { WorkflowWithInnerContext } from '@/app/components/workflow'
|
||||
import { useAvailableNodesMetaData } from '@/app/components/workflow-app/hooks'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { getKeyboardKeyCodeBySystem } from '@/app/components/workflow/utils'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { usePublishSnippetWorkflowMutation } from '@/service/use-snippet-workflows'
|
||||
import { useConfigsMap } from '../hooks/use-configs-map'
|
||||
import { useNodesSyncDraft } from '../hooks/use-nodes-sync-draft'
|
||||
import { useSnippetRefreshDraft } from '../hooks/use-snippet-refresh-draft'
|
||||
@ -61,6 +67,7 @@ const SnippetMain = ({
|
||||
const media = useBreakpoints()
|
||||
const isMobile = media === MediaType.mobile
|
||||
const [fields, setFields] = useState<SnippetInputField[]>(payload.inputFields)
|
||||
const publishSnippetMutation = usePublishSnippetWorkflowMutation(snippetId)
|
||||
const {
|
||||
doSyncWorkflowDraft,
|
||||
syncInputFieldsDraft,
|
||||
@ -97,8 +104,8 @@ const SnippetMain = ({
|
||||
openEditor,
|
||||
reset,
|
||||
setInputPanelOpen,
|
||||
setPublishMenuOpen,
|
||||
toggleInputPanel,
|
||||
togglePublishMenu,
|
||||
} = useSnippetDetailStore(useShallow(state => ({
|
||||
editingField: state.editingField,
|
||||
isEditorOpen: state.isEditorOpen,
|
||||
@ -108,8 +115,8 @@ const SnippetMain = ({
|
||||
openEditor: state.openEditor,
|
||||
reset: state.reset,
|
||||
setInputPanelOpen: state.setInputPanelOpen,
|
||||
setPublishMenuOpen: state.setPublishMenuOpen,
|
||||
toggleInputPanel: state.toggleInputPanel,
|
||||
togglePublishMenu: state.togglePublishMenu,
|
||||
})))
|
||||
|
||||
useEffect(() => {
|
||||
@ -166,6 +173,27 @@ const SnippetMain = ({
|
||||
setInputPanelOpen(false)
|
||||
}
|
||||
|
||||
const handlePublish = useCallback(async () => {
|
||||
try {
|
||||
await publishSnippetMutation.mutateAsync({
|
||||
params: { snippetId },
|
||||
})
|
||||
setPublishMenuOpen(false)
|
||||
toast.success(t('publishSuccess'))
|
||||
}
|
||||
catch (error) {
|
||||
toast.error(error instanceof Error ? error.message : t('publishFailed'))
|
||||
}
|
||||
}, [publishSnippetMutation, setPublishMenuOpen, snippetId, t])
|
||||
|
||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.shift.p`, (e) => {
|
||||
if (section !== 'orchestrate' || publishSnippetMutation.isPending)
|
||||
return
|
||||
|
||||
e.preventDefault()
|
||||
void handlePublish()
|
||||
}, { exactMatch: true, useCapture: true })
|
||||
|
||||
const hooksStore = useMemo(() => {
|
||||
return {
|
||||
doSyncWorkflowDraft,
|
||||
@ -222,9 +250,11 @@ const SnippetMain = ({
|
||||
isEditorOpen={isEditorOpen}
|
||||
isInputPanelOpen={isInputPanelOpen}
|
||||
isPublishMenuOpen={isPublishMenuOpen}
|
||||
isPublishing={publishSnippetMutation.isPending}
|
||||
onToggleInputPanel={handleToggleInputPanel}
|
||||
onTogglePublishMenu={togglePublishMenu}
|
||||
onPublishMenuOpenChange={setPublishMenuOpen}
|
||||
onCloseInputPanel={handleCloseInputPanel}
|
||||
onPublish={handlePublish}
|
||||
onOpenEditor={openEditor}
|
||||
onCloseEditor={closeEditor}
|
||||
onSubmitField={handleSubmitField}
|
||||
|
||||
@ -24,7 +24,9 @@
|
||||
"panelSecondaryGroup": "Optional inputs",
|
||||
"panelTitle": "Input Field",
|
||||
"publishButton": "Publish",
|
||||
"publishFailed": "Failed to publish snippet",
|
||||
"publishMenuCurrentDraft": "Current draft unpublished",
|
||||
"publishSuccess": "Snippet published",
|
||||
"save": "Save",
|
||||
"sectionEvaluation": "Evaluation",
|
||||
"sectionOrchestrate": "Orchestrate",
|
||||
|
||||
@ -24,7 +24,9 @@
|
||||
"panelSecondaryGroup": "可选输入",
|
||||
"panelTitle": "输入字段",
|
||||
"publishButton": "发布",
|
||||
"publishFailed": "发布 Snippet 失败",
|
||||
"publishMenuCurrentDraft": "当前草稿未发布",
|
||||
"publishSuccess": "Snippet 已发布",
|
||||
"save": "保存",
|
||||
"sectionEvaluation": "评测",
|
||||
"sectionOrchestrate": "编排",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user