This is an automated email from the ASF dual-hosted git repository.
klesh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-devlake.git
The following commit(s) were added to refs/heads/main by this push:
new c39684335 blueprint and project 404 redirection & UI message (#8556)
c39684335 is described below
commit c39684335991bcd8550b0f8656ddecedac8372ab
Author: Veet Moradiya <[email protected]>
AuthorDate: Wed Aug 27 08:53:33 2025 +0530
blueprint and project 404 redirection & UI message (#8556)
---
config-ui/src/hooks/use-refresh-data.ts | 9 +++++---
.../routes/blueprint/detail/blueprint-detail.tsx | 22 +++++++++++++++-----
config-ui/src/routes/project/detail/index.tsx | 24 ++++++++++++++++++----
3 files changed, 43 insertions(+), 12 deletions(-)
diff --git a/config-ui/src/hooks/use-refresh-data.ts
b/config-ui/src/hooks/use-refresh-data.ts
index 34fc62474..5e1fa7a0f 100644
--- a/config-ui/src/hooks/use-refresh-data.ts
+++ b/config-ui/src/hooks/use-refresh-data.ts
@@ -26,6 +26,7 @@ export const useRefreshData = <T>(request: (signal:
AbortSignal) => Promise<T>,
state: 'ready' | 'pending' | 'error';
deps?: React.DependencyList;
data?: T;
+ error?: unknown;
abortController?: AbortController;
timer?: number;
}>({
@@ -37,15 +38,14 @@ export const useRefreshData = <T>(request: (signal:
AbortSignal) => Promise<T>,
data: ref.current.data,
ready: ref.current.state === 'ready',
pending: ref.current.state === 'pending',
- error: ref.current.state === 'error',
+ error: ref.current.error,
};
}
- // When the last state transition has not waited until the new request is
completed
- // Reset status to pending
ref.current.state = 'pending';
ref.current.deps = deps;
ref.current.data = undefined;
+ ref.current.error = undefined;
clearTimeout(ref.current.timer);
ref.current.abortController?.abort();
ref.current.timer = window.setTimeout(() => {
@@ -54,6 +54,7 @@ export const useRefreshData = <T>(request: (signal:
AbortSignal) => Promise<T>,
.then((data: T) => {
ref.current.data = data;
ref.current.state = 'ready';
+ ref.current.error = undefined;
setVersion((v) => v + 1);
})
.catch((err: unknown) => {
@@ -61,6 +62,7 @@ export const useRefreshData = <T>(request: (signal:
AbortSignal) => Promise<T>,
return;
}
ref.current.state = 'error';
+ ref.current.error = err;
console.error(err);
setVersion((v) => v + 1);
});
@@ -69,5 +71,6 @@ export const useRefreshData = <T>(request: (signal:
AbortSignal) => Promise<T>,
return {
ready: false,
pending: true,
+ error: undefined,
};
};
diff --git a/config-ui/src/routes/blueprint/detail/blueprint-detail.tsx
b/config-ui/src/routes/blueprint/detail/blueprint-detail.tsx
index 78a7b6e4f..52491bac9 100644
--- a/config-ui/src/routes/blueprint/detail/blueprint-detail.tsx
+++ b/config-ui/src/routes/blueprint/detail/blueprint-detail.tsx
@@ -17,12 +17,13 @@
*/
import { useEffect, useState } from 'react';
-import { useLocation } from 'react-router-dom';
-import { Tabs } from 'antd';
-
+import { useLocation, useNavigate } from 'react-router-dom';
+import { Tabs, message } from 'antd';
+import axios from 'axios';
import API from '@/api';
import { PageLoading } from '@/components';
import { useRefreshData } from '@/hooks';
+import { PATHS } from '@/config';
import { FromEnum } from '../types';
@@ -40,16 +41,27 @@ export const BlueprintDetail = ({ id, from }: Props) => {
const [activeKey, setActiveKey] = useState('status');
const { state } = useLocation();
+ const navigate = useNavigate();
useEffect(() => {
setActiveKey(state?.activeKey ?? 'status');
}, [state]);
- const { ready, data } = useRefreshData(async () => {
- const [bpRes, pipelineRes] = await Promise.all([API.blueprint.get(id),
API.blueprint.pipelines(id)]);
+ const { ready, data, error } = useRefreshData(async () => {
+ const [bpRes, pipelineRes] = await Promise.all([
+ API.blueprint.get(id),
+ API.blueprint.pipelines(id),
+ ]);
return [bpRes, pipelineRes.pipelines[0]];
}, [version]);
+ useEffect(() => {
+ if (axios.isAxiosError(error) && error.response?.status === 404) {
+ message.error(`Blueprint not found with id: ${id}`);
+ navigate(PATHS.BLUEPRINTS(), { replace: true });
+ }
+ }, [error, navigate, id]);
+
const handlRefresh = () => {
setVersion((v) => v + 1);
};
diff --git a/config-ui/src/routes/project/detail/index.tsx
b/config-ui/src/routes/project/detail/index.tsx
index fb37a68eb..7c6a8b2ba 100644
--- a/config-ui/src/routes/project/detail/index.tsx
+++ b/config-ui/src/routes/project/detail/index.tsx
@@ -17,9 +17,11 @@
*/
import { useEffect, useState } from 'react';
-import { useParams, useLocation } from 'react-router-dom';
+import { useParams, useLocation, useNavigate } from 'react-router-dom';
+import axios from 'axios';
+
import { Helmet } from 'react-helmet';
-import { Tabs } from 'antd';
+import { Tabs, message } from 'antd';
import API from '@/api';
import { PageHeader, PageLoading } from '@/components';
@@ -39,12 +41,22 @@ export const ProjectDetailPage = () => {
const { pname } = useParams() as { pname: string };
const { state } = useLocation();
+ const navigate = useNavigate();
useEffect(() => {
setTabId(state?.tabId ?? 'blueprint');
}, [state]);
- const { ready, data } = useRefreshData(() => API.project.get(pname), [pname,
version]);
+ const { ready, data, error } = useRefreshData(() => API.project.get(pname),
[pname, version]);
+
+ useEffect(() => {
+ if (axios.isAxiosError(error) && error.response?.status === 404) {
+ message.error(`Project not found with project name: ${pname}`);
+ setTimeout(() => {
+ navigate(PATHS.PROJECTS(), { replace: true });
+ }, 100);
+ }
+ }, [error, navigate, pname]);
const handleChangeTabId = (tabId: string) => {
setTabId(tabId);
@@ -54,10 +66,14 @@ export const ProjectDetailPage = () => {
setVersion((v) => v + 1);
};
- if (!ready || !data) {
+ if (!ready && !error) {
return <PageLoading />;
}
+ if (!data) {
+ return null;
+ }
+
return (
<PageHeader
breadcrumbs={[