Publish histories (#1331)

* fix http plugin edge (#95)

* fix http plugin edge

* use getHandleId

* perf: i18n file

* feat: histories list

* perf: request lock

* fix: ts

* move box components

* fix: edit form refresh

---------

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
Archer
2024-04-30 12:42:13 +08:00
committed by GitHub
parent a0c1320d47
commit db6fc53840
46 changed files with 741 additions and 129 deletions

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { useTheme, type BoxProps } from '@chakra-ui/react';
import MyBox from '../common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
const PageContainer = ({
children,

View File

@@ -1,19 +0,0 @@
import React from 'react';
import { Box, BoxProps } from '@chakra-ui/react';
import Loading from '@fastgpt/web/components/common/MyLoading';
type Props = BoxProps & {
isLoading?: boolean;
text?: string;
};
const MyBox = ({ text, isLoading, children, ...props }: Props) => {
return (
<Box position={'relative'} {...props}>
{isLoading && <Loading fixed={false} text={text} />}
{children}
</Box>
);
};
export default MyBox;

View File

@@ -7,7 +7,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import MyTooltip from '@/components/MyTooltip';
import dynamic from 'next/dynamic';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { SearchScoreTypeEnum, SearchScoreTypeMap } from '@fastgpt/global/core/dataset/constants';
const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal'));

View File

@@ -5,7 +5,7 @@ import React, { Dispatch, useMemo, useState } from 'react';
import { useTranslation } from 'next-i18next';
import { Box } from '@chakra-ui/react';
import ParentPaths from '@/components/common/ParentPaths';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
type PathItemType = {
parentId: string;

View File

@@ -14,7 +14,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { getPreviewPluginModule } from '@/web/core/plugin/api';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { moduleTemplatesList } from '@fastgpt/global/core/workflow/template/constants';
import { workflowNodeTemplateList } from '@fastgpt/web/core/workflow/constants';
import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs';
import { useWorkflowStore } from '@/web/core/workflow/store/workflow';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
@@ -259,7 +259,7 @@ const RenderList = React.memo(function RenderList({
const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes);
const formatTemplates = useMemo<nodeTemplateListType>(() => {
const copy: nodeTemplateListType = JSON.parse(JSON.stringify(moduleTemplatesList));
const copy: nodeTemplateListType = JSON.parse(JSON.stringify(workflowNodeTemplateList(t)));
templates.forEach((item) => {
const index = copy.findIndex((template) => template.type === item.templateType);
if (index === -1) return;

View File

@@ -0,0 +1,179 @@
import React, { useState } from 'react';
import { getPublishList, postRevertVersion } from '@/web/core/app/versionApi';
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
import CustomRightDrawer from '@fastgpt/web/components/common/MyDrawer/CustomRightDrawer';
import { useTranslation } from 'next-i18next';
import { useMemoizedFn } from 'ahooks';
import { Box, Button, Flex } from '@chakra-ui/react';
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../context';
import { useAppStore } from '@/web/core/app/store/useAppStore';
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import MyIcon from '@fastgpt/web/components/common/Icon';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
const PublishHistoriesSlider = () => {
const { t } = useTranslation();
const { openConfirm, ConfirmModal } = useConfirm({
content: t('core.workflow.publish.OnRevert version confirm')
});
const { appDetail, setAppDetail } = useAppStore();
const appId = useContextSelector(WorkflowContext, (e) => e.appId);
const setIsShowVersionHistories = useContextSelector(
WorkflowContext,
(e) => e.setIsShowVersionHistories
);
const initData = useContextSelector(WorkflowContext, (e) => e.initData);
const [selectedHistoryId, setSelectedHistoryId] = useState<string>();
const { list, ScrollList, isLoading } = useScrollPagination(getPublishList, {
itemHeight: 49,
overscan: 20,
pageSize: 30,
defaultParams: {
appId
}
});
const onClose = useMemoizedFn(() => {
setIsShowVersionHistories(false);
});
const onPreview = useMemoizedFn((data: AppVersionSchemaType) => {
setSelectedHistoryId(data._id);
initData({
nodes: data.nodes,
edges: data.edges
});
});
const onCloseSlider = useMemoizedFn(() => {
setSelectedHistoryId(undefined);
initData({
nodes: appDetail.modules,
edges: appDetail.edges
});
onClose();
});
const { mutate: onRevert, isLoading: isReverting } = useRequest({
mutationFn: async (data: AppVersionSchemaType) => {
if (!appId) return;
await postRevertVersion(appId, {
versionId: data._id,
editNodes: appDetail.modules,
editEdges: appDetail.edges
});
setAppDetail({
...appDetail,
modules: data.nodes,
edges: data.edges
});
onCloseSlider();
}
});
const showLoading = isLoading || isReverting;
return (
<>
<CustomRightDrawer
onClose={onCloseSlider}
iconSrc="core/workflow/versionHistories"
title={t('core.workflow.publish.histories')}
maxW={'300px'}
px={0}
showMask={false}
mt={'60px'}
overflow={'unset'}
>
<Button
mx={'20px'}
variant={'whitePrimary'}
mb={1}
isDisabled={!selectedHistoryId}
onClick={() => {
setSelectedHistoryId(undefined);
initData({
nodes: appDetail.modules,
edges: appDetail.edges
});
}}
>
{t('core.workflow.Current workflow')}
</Button>
<ScrollList isLoading={showLoading} flex={'1 0 0'} px={5}>
{list.map((data, index) => {
const item = data.data;
return (
<Flex
key={data.index}
alignItems={'center'}
py={4}
px={3}
borderRadius={'md'}
cursor={'pointer'}
fontWeight={500}
_hover={{
bg: 'primary.50'
}}
{...(selectedHistoryId === item._id && {
color: 'primary.600'
})}
onClick={() => onPreview(item)}
>
<Box
w={'12px'}
h={'12px'}
borderWidth={'2px'}
borderColor={'primary.600'}
borderRadius={'50%'}
position={'relative'}
{...(index !== list.length - 1 && {
_after: {
content: '""',
height: '40px',
width: '2px',
bgColor: 'myGray.250',
position: 'absolute',
top: '10px',
left: '3px'
}
})}
></Box>
<Box ml={3} flex={'1 0 0'}>
{formatTime2YMDHM(item.time)}
</Box>
{item._id === selectedHistoryId && (
<MyTooltip label={t('core.workflow.publish.OnRevert version')}>
<MyIcon
name={'core/workflow/revertVersion'}
w={'20px'}
color={'primary.600'}
onClick={(e) => {
e.stopPropagation();
openConfirm(() => onRevert(item))();
}}
/>
</MyTooltip>
)}
</Flex>
);
})}
</ScrollList>
</CustomRightDrawer>
<ConfirmModal />
</>
);
};
export default React.memo(PublishHistoriesSlider);

View File

@@ -36,10 +36,12 @@ import { createContext } from 'use-context-selector';
import { defaultRunningStatus } from './constants';
import { checkNodeRunStatus } from '@fastgpt/global/core/workflow/runtime/utils';
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
type WorkflowContextType = {
appId?: string;
mode: 'app' | 'plugin';
basicNodeTemplates: FlowNodeTemplateType[];
filterAppIds?: string[];
@@ -83,7 +85,6 @@ type WorkflowContextType = {
};
initData: (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => Promise<void>;
// debug
// debug
workflowDebugData:
| {
@@ -103,6 +104,10 @@ type WorkflowContextType = {
runtimeEdges: RuntimeEdgeItemType[];
}) => Promise<void>;
onStopNodeDebug: () => void;
// version history
isShowVersionHistories: boolean;
setIsShowVersionHistories: React.Dispatch<React.SetStateAction<boolean>>;
};
type ContextValueProps = Pick<
@@ -201,6 +206,10 @@ export const WorkflowContext = createContext<WorkflowContextType>({
},
onChangeNode: function (e: FlowNodeChangeProps): void {
throw new Error('Function not implemented.');
},
isShowVersionHistories: false,
setIsShowVersionHistories: function (value: React.SetStateAction<boolean>): void {
throw new Error('Function not implemented.');
}
});
@@ -616,6 +625,10 @@ const WorkflowContextProvider = ({
[onNextNodeDebug, onStopNodeDebug]
);
/* Version histories */
const [isShowVersionHistories, setIsShowVersionHistories] = useState(false);
/* event bus */
useEffect(() => {
eventBus.on(EventNameEnum.requestWorkflowStore, () => {
eventBus.emit(EventNameEnum.receiveWorkflowStore, {
@@ -630,6 +643,7 @@ const WorkflowContextProvider = ({
return (
<WorkflowContext.Provider
value={{
appId,
reactFlowWrapper,
...value,
// node
@@ -661,7 +675,11 @@ const WorkflowContextProvider = ({
workflowDebugData,
onNextNodeDebug,
onStartNodeDebug,
onStopNodeDebug
onStopNodeDebug,
// version history
isShowVersionHistories,
setIsShowVersionHistories
}}
>
{children}