FastGPT/projects/app/src/pageComponents/app/detail/context.tsx
Archer af669a1cfc
4.14.4 features (#6090)
* perf: zod with app log (#6083)

* perf: safe decode

* perf: zod with app log

* fix: text

* remove log

* rename field

* refactor: improve like/dislike interaction (#6080)

* refactor: improve like/dislike interaction

* button style & merge status

* perf

* fix

* i18n

* feedback ui

* format

* api optimize

* openapi

* read status

---------

Co-authored-by: archer <545436317@qq.com>

* perf: remove empty chat

* perf: delete resource tip

* fix: confirm

* feedback filter

* fix: ts

* perf: linker scroll

* perf: feedback ui

* fix: plugin file input store

* fix: max tokens

* update comment

* fix: condition value type

* fix feedback (#6095)

* fix feedback

* text

* list

* fix: versionid

---------

Co-authored-by: archer <545436317@qq.com>

* fix: chat setting render;export logs filter

* add test

* perf: log list api

* perf: redirect check

* perf: log list

* create ui

* create ui

---------

Co-authored-by: heheer <heheer@sealos.io>
2025-12-15 23:36:54 +08:00

263 lines
7.2 KiB
TypeScript

import {
type Dispatch,
type ReactNode,
type SetStateAction,
useCallback,
useMemo,
useState
} from 'react';
import { createContext } from 'use-context-selector';
import { defaultApp } from '@/web/core/app/constants';
import { delAppById, getAppDetailById, putAppById } from '@/web/core/app/api';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { type AppChatConfigType, type AppDetailType } from '@fastgpt/global/core/app/type';
import { type AppUpdateParams, type PostPublishAppProps } from '@/global/core/app/api';
import { postPublishApp, getAppLatestVersion } from '@/web/core/app/api/version';
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import dynamic from 'next/dynamic';
import { useDisclosure } from '@chakra-ui/react';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import type { StoreEdgeItemType } from '@fastgpt/global/core/workflow/type/edge';
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
import { useToast } from '@fastgpt/web/hooks/useToast';
const InfoModal = dynamic(() => import('./InfoModal'));
const TagsEditModal = dynamic(() => import('./TagsEditModal'));
export enum TabEnum {
'appEdit' = 'appEdit',
'publish' = 'publish',
'logs' = 'logs'
}
type AppContextType = {
appId: string;
currentTab: TabEnum;
route2Tab: (currentTab: TabEnum) => void;
appDetail: AppDetailType;
setAppDetail: Dispatch<SetStateAction<AppDetailType>>;
loadingApp: boolean;
updateAppDetail: (data: AppUpdateParams) => Promise<void>;
onOpenInfoEdit: () => void;
onOpenTeamTagModal: () => void;
onDelApp: () => void;
onSaveApp: (data: PostPublishAppProps) => Promise<void>;
appLatestVersion:
| {
nodes: StoreNodeItemType[];
edges: StoreEdgeItemType[];
chatConfig: AppChatConfigType;
}
| undefined;
reloadAppLatestVersion: () => void;
reloadApp: () => void;
};
export const AppContext = createContext<AppContextType>({
appId: '',
currentTab: TabEnum.appEdit,
route2Tab: function (currentTab: TabEnum): void {
throw new Error('Function not implemented.');
},
appDetail: defaultApp,
loadingApp: false,
updateAppDetail: function (data: AppUpdateParams): Promise<void> {
throw new Error('Function not implemented.');
},
setAppDetail: function (value: SetStateAction<AppDetailType>): void {
throw new Error('Function not implemented.');
},
onOpenInfoEdit: function (): void {
throw new Error('Function not implemented.');
},
onOpenTeamTagModal: function (): void {
throw new Error('Function not implemented.');
},
onDelApp: function (): void {
throw new Error('Function not implemented.');
},
onSaveApp: function (data: PostPublishAppProps): Promise<void> {
throw new Error('Function not implemented.');
},
appLatestVersion: undefined,
reloadAppLatestVersion: function (): void {
throw new Error('Function not implemented.');
},
reloadApp: function (): void {
throw new Error('Function not implemented.');
}
});
const AppContextProvider = ({ children }: { children: ReactNode }) => {
const { t } = useTranslation();
const { toast } = useToast();
const router = useRouter();
const { appId, currentTab = TabEnum.appEdit } = router.query as {
appId: string;
currentTab: TabEnum;
};
const {
isOpen: isOpenInfoEdit,
onOpen: onOpenInfoEdit,
onClose: onCloseInfoEdit
} = useDisclosure();
const {
isOpen: isOpenTeamTagModal,
onOpen: onOpenTeamTagModal,
onClose: onCloseTeamTagModal
} = useDisclosure();
const route2Tab = useCallback(
(currentTab: `${TabEnum}`) => {
router.push({
query: {
...router.query,
currentTab
}
});
},
[router]
);
const [appDetail, setAppDetail] = useState<AppDetailType>(defaultApp);
const { loading: loadingApp, runAsync: reloadApp } = useRequest2(
() => {
if (appId) {
return getAppDetailById(appId);
}
return Promise.resolve(defaultApp);
},
{
manual: false,
refreshDeps: [appId],
errorToast: t('common:core.app.error.Get app failed'),
onError(err: any) {
router.replace('/dashboard/agent');
},
onSuccess(res) {
setAppDetail(res);
}
}
);
const { data: appLatestVersion, run: reloadAppLatestVersion } = useRequest2(
() => getAppLatestVersion({ appId }),
{
manual: !appDetail?.permission?.hasWritePer,
refreshDeps: [appDetail?.permission?.hasWritePer]
}
);
const { runAsync: updateAppDetail } = useRequest2(async (data: AppUpdateParams) => {
await putAppById(appId, data);
setAppDetail((state) => ({
...state,
...data,
modules: data.nodes || state.modules
}));
});
const { runAsync: onSaveApp } = useRequest2(
async (data: PostPublishAppProps) => {
try {
if (!appDetail.permission.hasWritePer) return;
await postPublishApp(appId, data);
setAppDetail((state) => ({
...state,
...data,
modules: data.nodes || state.modules
}));
reloadAppLatestVersion();
} catch (error: any) {
if (error.statusText == AppErrEnum.unExist) {
return;
}
return Promise.reject(error);
}
},
{
manual: true,
refreshDeps: [appDetail.permission.hasWritePer, appId]
}
);
const { openConfirm: openConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({
content: t('app:confirm_del_app_tip', { name: appDetail.name }),
type: 'delete'
});
const { runAsync: deleteApp } = useRequest2(
async () => {
if (!appDetail) return Promise.reject('Not load app');
return delAppById(appDetail._id);
},
{
onSuccess(data) {
data.forEach((appId) => {
localStorage.removeItem(`app_log_keys_${appId}`);
});
router.replace(`/dashboard/agent`);
},
successToast: t('common:delete_success'),
errorToast: t('common:delete_failed')
}
);
const onDelApp = useCallback(
() =>
openConfirmDel({
onConfirm: deleteApp,
customContent: t('app:confirm_del_app_tip', { name: appDetail.name })
})(),
[appDetail.name, deleteApp, openConfirmDel, t]
);
const contextValue: AppContextType = useMemo(
() => ({
appId,
currentTab,
route2Tab,
appDetail,
setAppDetail,
loadingApp,
updateAppDetail,
onOpenInfoEdit,
onOpenTeamTagModal,
onDelApp,
onSaveApp,
appLatestVersion,
reloadAppLatestVersion,
reloadApp
}),
[
appDetail,
appId,
appLatestVersion,
currentTab,
loadingApp,
onDelApp,
onOpenInfoEdit,
onOpenTeamTagModal,
onSaveApp,
reloadApp,
reloadAppLatestVersion,
route2Tab,
updateAppDetail
]
);
return (
<AppContext.Provider value={contextValue}>
{children}
{isOpenInfoEdit && <InfoModal onClose={onCloseInfoEdit} />}
{isOpenTeamTagModal && <TagsEditModal onClose={onCloseTeamTagModal} />}
<ConfirmDelModal />
</AppContext.Provider>
);
};
export default AppContextProvider;