Merge pull request #2267 from automatisch/export-flow

feat: Implement initial logic of exporting flow
This commit is contained in:
Ömer Faruk Aydın
2025-01-13 18:06:55 +01:00
committed by GitHub
17 changed files with 475 additions and 31 deletions

View File

@@ -6,29 +6,36 @@ import Button from '@mui/material/Button';
import Tooltip from '@mui/material/Tooltip';
import IconButton from '@mui/material/IconButton';
import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
import DownloadIcon from '@mui/icons-material/Download';
import Snackbar from '@mui/material/Snackbar';
import { ReactFlowProvider } from 'reactflow';
import { EditorProvider } from 'contexts/Editor';
import EditableTypography from 'components/EditableTypography';
import Container from 'components/Container';
import Editor from 'components/Editor';
import Can from 'components/Can';
import useFormatMessage from 'hooks/useFormatMessage';
import * as URLS from 'config/urls';
import { TopBar } from './style';
import * as URLS from 'config/urls';
import Can from 'components/Can';
import Container from 'components/Container';
import EditableTypography from 'components/EditableTypography';
import Editor from 'components/Editor';
import EditorNew from 'components/EditorNew/EditorNew';
import useFlow from 'hooks/useFlow';
import useFormatMessage from 'hooks/useFormatMessage';
import useUpdateFlow from 'hooks/useUpdateFlow';
import useUpdateFlowStatus from 'hooks/useUpdateFlowStatus';
import EditorNew from 'components/EditorNew/EditorNew';
import useExportFlow from 'hooks/useExportFlow';
import useDownloadJsonAsFile from 'hooks/useDownloadJsonAsFile';
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
const useNewFlowEditor = process.env.REACT_APP_USE_NEW_FLOW_EDITOR === 'true';
export default function EditorLayout() {
const { flowId } = useParams();
const formatMessage = useFormatMessage();
const enqueueSnackbar = useEnqueueSnackbar();
const { mutateAsync: updateFlow } = useUpdateFlow(flowId);
const { mutateAsync: updateFlowStatus } = useUpdateFlowStatus(flowId);
const { mutateAsync: exportFlow } = useExportFlow(flowId);
const downloadJsonAsFile = useDownloadJsonAsFile();
const { data, isLoading: isFlowLoading } = useFlow(flowId);
const flow = data?.data;
@@ -38,6 +45,19 @@ export default function EditorLayout() {
});
};
const onExportFlow = async (name) => {
const flowExport = await exportFlow();
downloadJsonAsFile({
contents: flowExport.data,
name: flowExport.data.name,
});
enqueueSnackbar(formatMessage('flowEditor.flowSuccessfullyExported'), {
variant: 'success',
});
};
return (
<>
<TopBar
@@ -80,7 +100,23 @@ export default function EditorLayout() {
)}
</Box>
<Box pr={1}>
<Box pr={1} display="flex" gap={1}>
<Can I="read" a="Flow" passThrough>
{(allowed) => (
<Button
disabled={!allowed || !flow}
variant="outlined"
color="info"
size="small"
onClick={onExportFlow}
data-test="export-flow-button"
startIcon={<DownloadIcon />}
>
{formatMessage('flowEditor.export')}
</Button>
)}
</Can>
<Can I="publish" a="Flow" passThrough>
{(allowed) => (
<Button

View File

@@ -12,6 +12,8 @@ import * as URLS from 'config/urls';
import useFormatMessage from 'hooks/useFormatMessage';
import useDuplicateFlow from 'hooks/useDuplicateFlow';
import useDeleteFlow from 'hooks/useDeleteFlow';
import useExportFlow from 'hooks/useExportFlow';
import useDownloadJsonAsFile from 'hooks/useDownloadJsonAsFile';
function ContextMenu(props) {
const { flowId, onClose, anchorEl, onDuplicateFlow, onDeleteFlow, appKey } =
@@ -20,7 +22,9 @@ function ContextMenu(props) {
const formatMessage = useFormatMessage();
const queryClient = useQueryClient();
const { mutateAsync: duplicateFlow } = useDuplicateFlow(flowId);
const { mutateAsync: deleteFlow } = useDeleteFlow();
const { mutateAsync: deleteFlow } = useDeleteFlow(flowId);
const { mutateAsync: exportFlow } = useExportFlow(flowId);
const downloadJsonAsFile = useDownloadJsonAsFile();
const onFlowDuplicate = React.useCallback(async () => {
await duplicateFlow();
@@ -51,7 +55,7 @@ function ContextMenu(props) {
]);
const onFlowDelete = React.useCallback(async () => {
await deleteFlow(flowId);
await deleteFlow();
if (appKey) {
await queryClient.invalidateQueries({
@@ -65,7 +69,30 @@ function ContextMenu(props) {
onDeleteFlow?.();
onClose();
}, [flowId, onClose, deleteFlow, queryClient, onDeleteFlow]);
}, [
deleteFlow,
appKey,
enqueueSnackbar,
formatMessage,
onDeleteFlow,
onClose,
queryClient,
]);
const onFlowExport = React.useCallback(async () => {
const flowExport = await exportFlow();
downloadJsonAsFile({
contents: flowExport.data,
name: flowExport.data.name,
});
enqueueSnackbar(formatMessage('flow.successfullyExported'), {
variant: 'success',
});
onClose();
}, [exportFlow, downloadJsonAsFile, enqueueSnackbar, formatMessage, onClose]);
return (
<Menu
@@ -90,6 +117,14 @@ function ContextMenu(props) {
)}
</Can>
<Can I="read" a="Flow" passThrough>
{(allowed) => (
<MenuItem disabled={!allowed} onClick={onFlowExport}>
{formatMessage('flow.export')}
</MenuItem>
)}
</Can>
<Can I="delete" a="Flow" passThrough>
{(allowed) => (
<MenuItem disabled={!allowed} onClick={onFlowDelete}>