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

@@ -117,6 +117,7 @@
"Name is empty": "Name is empty",
"New Create": "Create",
"Next Step": "Next",
"No more data": "No more",
"Not open": "Close",
"Number of words": "{{amount}} words",
"OK": "OK",
@@ -1101,6 +1102,7 @@
"Check Failed": "Workflow verification fails. Check whether the node or connection is normal",
"Confirm stop debug": "Do you want to terminate debugging? Debugging information is not retained.",
"Copy node": "Copy node",
"Current workflow": "Current workflow",
"Custom inputs": "Inputs",
"Custom outputs": "Outputs",
"Custom variable": "Custom variable",
@@ -1148,6 +1150,11 @@
"target": "Target Data",
"textarea": "Textarea"
},
"publish": {
"OnRevert version": "Click back to that version",
"OnRevert version confirm": "Are you sure to roll back the version?",
"histories": "Publiish histories"
},
"tool": {
"Handle": "Tool handle",
"Select Tool": "Select Tool"

View File

@@ -117,6 +117,7 @@
"Name is empty": "名称不能为空",
"New Create": "新建",
"Next Step": "下一步",
"No more data": "没有更多了~",
"Not open": "未开启",
"Number of words": "{{amount}}字",
"OK": "好的",
@@ -1103,6 +1104,7 @@
"Check Failed": "工作流校验失败,请检查节点是否正确填值,以及连线是否正常",
"Confirm stop debug": "确认终止调试?调试信息将会不保留。",
"Copy node": "已复制节点",
"Current workflow": "当前工作流",
"Custom inputs": "自定义输入",
"Custom outputs": "自定义输出",
"Custom variable": "自定义变量",
@@ -1150,6 +1152,11 @@
"target": "外部数据",
"textarea": "多行输入框"
},
"publish": {
"OnRevert version": "点击回退到该版本",
"OnRevert version confirm": "确认回退该版本?",
"histories": "发布记录"
},
"tool": {
"Handle": "工具连接器",
"Select Tool": "选择工具"

View File

@@ -9,7 +9,6 @@ module.exports = {
locales: ['en', 'zh'],
localeDetection: false
},
localePath:
typeof window === 'undefined' ? require('path').resolve('./public/locales') : '/public/locales',
localePath: typeof window === 'undefined' ? require('path').resolve('./i18n') : '/i18n',
reloadOnPrerender: process.env.NODE_ENV === 'development'
};

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}

View File

@@ -25,3 +25,10 @@ export type PostPublishAppProps = {
nodes: AppSchema['modules'];
edges: AppSchema['edges'];
};
export type PostRevertAppProps = {
versionId: string;
// edit workflow
editNodes: AppSchema['modules'];
editEdges: AppSchema['edges'];
};

View File

@@ -26,7 +26,7 @@ import {
billTypeMap
} from '@fastgpt/global/support/wallet/bill/constants';
// import { usePagination } from '@/web/common/hooks/usePagination';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants';
import MySelect from '@fastgpt/web/components/common/MySelect';

View File

@@ -0,0 +1,34 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { NextAPI } from '@/service/middle/entry';
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
type Props = PaginationProps<{
appId: string;
}>;
type Response = PaginationResponse<AppVersionSchemaType>;
async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<Response> {
const { current, pageSize, appId } = req.body as Props;
const [result, total] = await Promise.all([
MongoAppVersion.find({
appId
})
.sort({
time: -1
})
.skip((current - 1) * pageSize)
.limit(pageSize),
MongoAppVersion.countDocuments({ appId })
]);
return {
total,
list: result
};
}
export default NextAPI(handler);

View File

@@ -0,0 +1,73 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { NextAPI } from '@/service/middle/entry';
import { authApp } from '@fastgpt/service/support/permission/auth/app';
import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { beforeUpdateAppFormat } from '@fastgpt/service/core/app/controller';
import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils';
import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time';
import { PostRevertAppProps } from '@/global/core/app/api';
type Response = {};
async function handler(req: NextApiRequest, res: NextApiResponse<any>): Promise<{}> {
const { appId } = req.query as { appId: string };
const { editNodes = [], editEdges = [], versionId } = req.body as PostRevertAppProps;
await authApp({ appId, req, per: 'w', authToken: true });
const version = await MongoAppVersion.findOne({
_id: versionId,
appId
});
if (!version) {
throw new Error('version not found');
}
const { nodes: formatEditNodes } = beforeUpdateAppFormat({ nodes: editNodes });
const { scheduledTriggerConfig } = splitGuideModule(getGuideModule(version.nodes));
await mongoSessionRun(async (session) => {
// 为编辑中的数据创建一个版本
await MongoAppVersion.create(
[
{
appId,
nodes: formatEditNodes,
edges: editEdges
}
],
{ session }
);
// 为历史版本再创建一个版本
await MongoAppVersion.create(
[
{
appId,
nodes: version.nodes,
edges: version.edges
}
],
{ session }
);
// update app
await MongoApp.findByIdAndUpdate(appId, {
modules: version.nodes,
edges: version.edges,
updateTime: new Date(),
scheduledTriggerConfig,
scheduledTriggerNextTime: scheduledTriggerConfig
? getNextTimeByCronStringAndTimezone(scheduledTriggerConfig)
: null
});
});
return {};
}
export default NextAPI(handler);

View File

@@ -22,12 +22,15 @@ import {
} from '@/web/core/workflow/utils';
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import { useQuery } from '@tanstack/react-query';
import { formatTime2HM } from '@fastgpt/global/common/string/time';
import { useContextSelector } from 'use-context-selector';
import { WorkflowContext, getWorkflowStore } from '@/components/core/workflow/context';
import { useInterval } from 'ahooks';
const ImportSettings = dynamic(() => import('@/components/core/workflow/Flow/ImportSettings'));
const PublishHistories = dynamic(
() => import('@/components/core/workflow/components/PublishHistoriesSlider')
);
type Props = { app: AppSchema; onClose: () => void };
@@ -55,7 +58,6 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
const { openConfirm: openConfigPublish, ConfirmModal } = useConfirm({
content: t('core.app.Publish Confirm')
});
const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure();
const { publishApp, updateAppDetail } = useAppStore();
const edges = useContextSelector(WorkflowContext, (v) => v.edges);
@@ -63,6 +65,17 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
const [saveLabel, setSaveLabel] = useState(t('core.app.Onclick to save'));
const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError);
const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure();
const isShowVersionHistories = useContextSelector(
WorkflowContext,
(v) => v.isShowVersionHistories
);
const setIsShowVersionHistories = useContextSelector(
WorkflowContext,
(v) => v.setIsShowVersionHistories
);
const flowData2StoreDataAndCheck = useCallback(async () => {
const { nodes } = await getWorkflowStore();
const checkResults = checkWorkflowNodeAndConnection({ nodes, edges });
@@ -81,6 +94,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
}, [edges, onUpdateNodeError, t, toast]);
const onclickSave = useCallback(async () => {
if (isShowVersionHistories) return;
const { nodes } = await getWorkflowStore();
if (nodes.length === 0) return null;
@@ -107,7 +121,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
setIsSaving(false);
return null;
}, [updateAppDetail, app._id, edges, t]);
}, [isShowVersionHistories, edges, updateAppDetail, app._id, t]);
const onclickPublish = useCallback(async () => {
setIsSaving(true);
@@ -160,15 +174,16 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
}
}, [copyData, flowData2StoreDataAndCheck, t]);
// effect
useBeforeunload({
callback: onclickSave,
tip: t('core.common.tip.leave page')
});
useQuery(['autoSave'], onclickSave, {
refetchInterval: 20 * 1000,
enabled: !!app._id
});
useInterval(() => {
if (!app._id) return;
onclickSave();
}, 20000);
const Render = useMemo(() => {
return (
@@ -180,6 +195,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
alignItems={'center'}
userSelect={'none'}
bg={'myGray.25'}
h={'67px'}
>
<IconButton
size={'smSquare'}
@@ -193,23 +209,25 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
isLoading={isSaving}
onClick={saveAndBack}
/>
<Box ml={[3, 5]}>
<Box ml={[2, 4]}>
<Box fontSize={['md', 'lg']} fontWeight={'bold'}>
{app.name}
</Box>
<MyTooltip label={t('core.app.Onclick to save')}>
<Box
fontSize={'sm'}
mt={1}
display={'inline-block'}
borderRadius={'xs'}
cursor={'pointer'}
onClick={onclickSave}
color={'myGray.500'}
>
{saveLabel}
</Box>
</MyTooltip>
{!isShowVersionHistories && (
<MyTooltip label={t('core.app.Onclick to save')}>
<Box
fontSize={'sm'}
mt={1}
display={'inline-block'}
borderRadius={'xs'}
cursor={'pointer'}
onClick={onclickSave}
color={'myGray.500'}
>
{saveLabel}
</Box>
</MyTooltip>
)}
</Box>
<Box flex={1} />
@@ -217,7 +235,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
<MyMenu
Button={
<IconButton
mr={[3, 5]}
mr={[2, 4]}
icon={<MyIcon name={'more'} w={'14px'} p={2} />}
aria-label={''}
size={'sm'}
@@ -238,10 +256,19 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
]}
/>
<Button
mr={[3, 5]}
<IconButton
mr={[2, 4]}
icon={<MyIcon name={'history'} w={'18px'} />}
aria-label={''}
size={'sm'}
leftIcon={<MyIcon name={'core/chat/chatLight'} w={['14px', '16px']} />}
w={'30px'}
variant={'whitePrimary'}
onClick={() => setIsShowVersionHistories(true)}
/>
<Button
size={'sm'}
leftIcon={<MyIcon name={'core/workflow/debug'} w={['14px', '16px']} />}
variant={'whitePrimary'}
onClick={async () => {
const data = await flowData2StoreDataAndCheck();
@@ -250,17 +277,20 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
}
}}
>
{t('core.Chat test')}
{t('core.workflow.Debug')}
</Button>
<Button
size={'sm'}
isLoading={isSaving}
leftIcon={<MyIcon name={'common/publishFill'} w={['14px', '16px']} />}
onClick={openConfigPublish(onclickPublish)}
>
{t('core.app.Publish')}
</Button>
{!isShowVersionHistories && (
<Button
ml={[2, 4]}
size={'sm'}
isLoading={isSaving}
leftIcon={<MyIcon name={'common/publishFill'} w={['14px', '16px']} />}
onClick={openConfigPublish(onclickPublish)}
>
{t('core.app.Publish')}
</Button>
)}
</Flex>
<ConfirmModal confirmText={t('core.app.Publish')} />
</>
@@ -275,8 +305,10 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
onclickPublish,
onclickSave,
openConfigPublish,
isShowVersionHistories,
saveAndBack,
saveLabel,
setIsShowVersionHistories,
setWorkflowTestData,
t,
theme.borders.base
@@ -286,6 +318,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({
<>
{Render}
{isOpenImport && <ImportSettings onClose={onCloseImport} />}
{isShowVersionHistories && <PublishHistories />}
</>
);
});

View File

@@ -28,7 +28,7 @@ import { getInitChatInfo } from '@/web/core/chat/api';
import Tag from '@fastgpt/web/components/common/Tag/index';
import MyModal from '@fastgpt/web/components/common/MyModal';
import { addDays } from 'date-fns';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { usePagination } from '@fastgpt/web/hooks/usePagination';
import DateRangePicker, { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker';
import { formatChatValue2InputType } from '@/components/ChatBox/utils';

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useTransition } from 'react';
import React, { useEffect, useMemo, useTransition } from 'react';
import { Box, Flex, Grid, BoxProps, useTheme, useDisclosure, Button } from '@chakra-ui/react';
import { AddIcon, QuestionOutlineIcon, SmallAddIcon } from '@chakra-ui/icons';
import { useFieldArray, UseFormReturn } from 'react-hook-form';
@@ -28,6 +28,7 @@ import type { SettingAIDataType } from '@fastgpt/global/core/app/type.d';
import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete';
import { TTSTypeEnum } from '@/constants/app';
import { getSystemVariables } from '@/web/core/app/utils';
import { useUpdate } from 'ahooks';
const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal'));
const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal'));
@@ -65,6 +66,7 @@ const EditForm = ({
const { allDatasets } = useDatasetStore();
const { llmModelList } = useSystemStore();
const [, startTst] = useTransition();
const refresh = useUpdate();
const { setValue, getValues, handleSubmit, control, watch } = editForm;
@@ -72,7 +74,6 @@ const EditForm = ({
control,
name: 'dataset.datasets'
});
const selectedTools = watch('selectedTools');
const {
isOpen: isOpenDatasetSelect,
@@ -106,6 +107,7 @@ const EditForm = ({
const tts = getValues('userGuide.tts');
const whisperConfig = getValues('userGuide.whisper');
const postQuestionGuide = getValues('userGuide.questionGuide');
const selectedTools = watch('selectedTools');
const selectDatasets = useMemo(
() => allDatasets.filter((item) => datasets.find((dataset) => dataset.datasetId === item._id)),
@@ -131,6 +133,16 @@ const EditForm = ({
errorToast: t('common.Save Failed')
});
useEffect(() => {
const wat = watch((data) => {
refresh();
});
return () => {
wat.unsubscribe();
};
}, []);
return (
<Box>
{/* title */}
@@ -459,7 +471,9 @@ const EditForm = ({
{isOpenToolsSelect && (
<ToolSelectModal
selectedTools={selectedTools}
onAddTool={(e) => setValue('selectedTools', [...selectedTools, e])}
onAddTool={(e) => {
setValue('selectedTools', [...selectedTools, e]);
}}
onRemoveTool={(e) => {
setValue(
'selectedTools',

View File

@@ -29,7 +29,7 @@ import Avatar from '@/components/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { AddIcon } from '@chakra-ui/icons';
import { getPreviewPluginModule } from '@/web/core/plugin/api';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
import ParentPaths from '@/components/common/ParentPaths';
import { PluginTypeEnum } from '@fastgpt/global/core/plugin/constants';

View File

@@ -23,7 +23,6 @@ const MyApps = () => {
const router = useRouter();
const { userInfo } = useUserStore();
const { myApps, loadMyApps } = useAppStore();
const [teamsTags, setTeamTags] = useState([]);
const { openConfirm, ConfirmModal } = useConfirm({
title: '删除提示',
content: '确认删除该应用所有信息?'

View File

@@ -26,7 +26,7 @@ import { getInitOutLinkChatInfo } from '@/web/core/chat/api';
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
import { useChatStore } from '@/web/core/chat/storeChat';
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
import { addLog } from '@fastgpt/service/common/system/log';

View File

@@ -32,7 +32,7 @@ import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
import { getErrText } from '@fastgpt/global/common/error/utils';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
import SliderApps from './components/SliderApps';
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';

View File

@@ -60,7 +60,7 @@ import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { useDatasetStore } from '@/web/core/dataset/store/dataset';
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
import { DatasetCollectionSyncResultEnum } from '@fastgpt/global/core/dataset/constants';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { usePagination } from '@fastgpt/web/hooks/usePagination';
import { ImportDataSourceEnum } from '@fastgpt/global/core/dataset/constants';

View File

@@ -1,4 +1,4 @@
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { Box, FlexProps } from '@chakra-ui/react';

View File

@@ -23,7 +23,7 @@ import DeleteIcon from '@fastgpt/web/components/common/Icon/delete';
import { defaultCollectionDetail } from '@/constants/dataset';
import { getDocPath } from '@/web/common/system/doc';
import RawSourceBox from '@/components/core/dataset/RawSourceBox';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';

View File

@@ -27,7 +27,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import { useRequest } from '@fastgpt/web/hooks/useRequest';
import DatasetTypeTag from '@/components/core/dataset/DatasetTypeTag';
import Head from 'next/head';
import MyBox from '@/components/common/MyBox';
import MyBox from '@fastgpt/web/components/common/MyBox';
const DataCard = dynamic(() => import('./components/DataCard'));
const Test = dynamic(() => import('./components/Test'));

View File

@@ -1,5 +1,5 @@
import 'i18next';
//import common from '../../public/locales/en/common.json';
//import common from '../../i18n/en/common.json';
interface I18nNamespaces {
common: any;

View File

@@ -16,6 +16,7 @@ type State = {
updateAppDetail(appId: string, data: AppUpdateParams): Promise<void>;
publishApp(appId: string, data: PostPublishAppProps): Promise<void>;
clearAppModules(): void;
setAppDetail(data: AppDetailType): void;
};
export const useAppStore = create<State>()(
@@ -61,6 +62,11 @@ export const useAppStore = create<State>()(
};
});
},
setAppDetail(data: AppDetailType) {
set((state) => {
state.appDetail = data;
});
},
clearAppModules() {
set((state) => {

View File

@@ -1,5 +1,13 @@
import { PostPublishAppProps } from '@/global/core/app/api';
import { PostPublishAppProps, PostRevertAppProps } from '@/global/core/app/api';
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
export const postPublishApp = (appId: string, data: PostPublishAppProps) =>
POST(`/core/app/version/publish?appId=${appId}`, data);
export const getPublishList = (data: PaginationProps<{ appId: string }>) =>
POST<PaginationResponse<AppVersionSchemaType>>('/core/app/version/list', data);
export const postRevertVersion = (appId: string, data: PostRevertAppProps) =>
POST(`/core/app/version/revert?appId=${appId}`, data);