diff --git a/plugin/src/client.ts b/plugin/src/client.ts index 2738d2c..45edfed 100644 --- a/plugin/src/client.ts +++ b/plugin/src/client.ts @@ -268,6 +268,22 @@ export const deleteTemplate = async (templateUuid: string): Promise => { } } +export const exportTemplateAsJson = async (templateUuid: string): Promise => { + const url = `${getApiBaseUrl()}/templates/${encodeURIComponent(templateUuid)}/export` + const response = await apiFetch(url) + + if (!response.ok) { + const data = await readApiResponse<{ detail?: string }>(response, url).catch(() => ({})) + throw new Error( + 'detail' in data && data.detail + ? data.detail + : 'Failed to export the template as JSON.', + ) + } + + return response.blob() +} + export const exportPipelineResultAsDocx = async ( runId: string, resultMarkdown: string, diff --git a/plugin/src/components/TemplateManager.tsx b/plugin/src/components/TemplateManager.tsx index 8c1c535..791c920 100644 --- a/plugin/src/components/TemplateManager.tsx +++ b/plugin/src/components/TemplateManager.tsx @@ -1,7 +1,7 @@ import { useCallback, useEffect, useState } from 'react' import { toast } from 'sonner' -import { getTemplate } from '@/client' +import { exportTemplateAsJson, getTemplate } from '@/client' import { CustomTemplateSection } from '@/components/CustomTemplateSection' import styles from '@/components/TemplateManager.module.css' import { TemplatePreview } from '@/components/TemplatePreview' @@ -36,6 +36,7 @@ export function TemplateManager({ const [isEditing, setIsEditing] = useState(false) const [detail, setDetail] = useState(null) const [isLoadingDetail, setIsLoadingDetail] = useState(false) + const [isExporting, setIsExporting] = useState(false) const selected = templates.find((template) => template.uuid === selectedUuid) const canManageSelected = selected?.scope === 'personal' @@ -143,6 +144,35 @@ export function TemplateManager({ } }, [selected, deleteByUuid, onSelectedUuidChange, resetDetail]) + const handleExport = useCallback(async () => { + if (!selected) { + return + } + + setIsExporting(true) + try { + const blob = await exportTemplateAsJson(selected.uuid) + const fileName = `${selected.title}.json` + const url = URL.createObjectURL(blob) + const link = document.createElement('a') + link.href = url + link.download = fileName + document.body.appendChild(link) + link.click() + link.remove() + URL.revokeObjectURL(url) + toast.success('Template JSON download has started.') + } catch (exportError) { + toast.error( + exportError instanceof Error + ? exportError.message + : 'Failed to export the template as JSON.', + ) + } finally { + setIsExporting(false) + } + }, [selected]) + return ( <> {detail && !isEditingSelected ? ( @@ -158,11 +188,20 @@ export function TemplateManager({ type="button" className="btn btn-outline-secondary with-icon" onClick={startEditing} - disabled={isDeleting} + disabled={isDeleting || isExporting} >