diff --git a/airflow/ui/package.json b/airflow/ui/package.json index c64090c0423ee..8788aa2f25425 100644 --- a/airflow/ui/package.json +++ b/airflow/ui/package.json @@ -23,7 +23,8 @@ "react-hot-loader": "^4", "react-icons": "^4.2.0", "react-query": "^3.12.3", - "react-router-dom": "^5.2.0" + "react-router-dom": "^5.2.0", + "use-react-router": "^1.0.7" }, "devDependencies": { "@neutrinojs/eslint": "^9.5.0", diff --git a/airflow/ui/src/App.tsx b/airflow/ui/src/App.tsx index c7ef9c656e101..8f7d82782bbd4 100644 --- a/airflow/ui/src/App.tsx +++ b/airflow/ui/src/App.tsx @@ -24,7 +24,21 @@ import { Route, Redirect, Switch } from 'react-router-dom'; import PrivateRoute from 'auth/PrivateRoute'; import Pipelines from 'views/Pipelines'; -import Pipeline from 'views/Pipeline'; + +import Details from 'views/Pipeline/runs/Details'; +import Code from 'views/Pipeline/runs/Code'; +import TaskTries from 'views/Pipeline/runs/TaskTries'; +import TaskDuration from 'views/Pipeline/runs/TaskDuration'; +import LandingTimes from 'views/Pipeline/runs/LandingTimes'; + +import Graph from 'views/Pipeline/run/Graph'; +import Gantt from 'views/Pipeline/run/Gantt'; + +import TIDetails from 'views/Pipeline/ti/Details'; +import RenderedTemplate from 'views/Pipeline/ti/RenderedTemplate'; +import RenderedK8s from 'views/Pipeline/ti/RenderedK8s'; +import Log from 'views/Pipeline/ti/Log'; +import XCom from 'views/Pipeline/ti/XCom'; import EventLogs from 'views/Activity/EventLogs'; import Runs from 'views/Activity/Runs'; @@ -51,7 +65,24 @@ const App = () => ( - + + + + + + + + + + + + + + + + + + diff --git a/airflow/ui/src/api/defaults.ts b/airflow/ui/src/api/defaults.ts index a0f0a59fc7cfa..a1979cc4398ed 100644 --- a/airflow/ui/src/api/defaults.ts +++ b/airflow/ui/src/api/defaults.ts @@ -20,3 +20,7 @@ export const defaultVersion = { version: '', gitVersion: '' }; export const defaultDags = { dags: [], totalEntries: 0 }; + +export const defaultDagRuns = { dagRuns: [], totalEntries: 0 }; + +export const defaultTaskInstances = { taskInstances: [], totalEntries: 0 }; diff --git a/airflow/ui/src/api/index.ts b/airflow/ui/src/api/index.ts index 6d06936733b65..cfcd52d1154a1 100644 --- a/airflow/ui/src/api/index.ts +++ b/airflow/ui/src/api/index.ts @@ -21,8 +21,8 @@ import axios, { AxiosResponse } from 'axios'; import { useQuery } from 'react-query'; import humps from 'humps'; -import type { Version } from 'interfaces'; -import type { DagsResponse } from 'interfaces/api'; +import type { Dag, DagRun, Version } from 'interfaces'; +import type { DagsResponse, DagRunsResponse, TaskInstancesResponse } from 'interfaces/api'; axios.defaults.baseURL = `${process.env.WEBSERVER_URL}/api/v1`; axios.interceptors.response.use( @@ -39,6 +39,23 @@ export function useDags() { ); } +export function useDagRuns(dagId: Dag['dagId'], dateMin?: string) { + return useQuery( + ['dagRun', dagId], + (): Promise => axios.get(`dags/${dagId}/dagRuns${dateMin ? `?start_date_gte=${dateMin}` : ''}`), + { refetchInterval }, + ); +} + +export function useTaskInstances(dagId: Dag['dagId'], dagRunId: DagRun['dagRunId'], dateMin?: string) { + return useQuery( + ['taskInstance', dagRunId], + (): Promise => ( + axios.get(`dags/${dagId}/dagRuns/${dagRunId}/taskInstances${dateMin ? `?start_date_gte=${dateMin}` : ''}`) + ), + ); +} + export function useVersion() { return useQuery( 'version', diff --git a/airflow/ui/src/components/PipelineBreadcrumb.tsx b/airflow/ui/src/components/PipelineBreadcrumb.tsx new file mode 100644 index 0000000000000..c3ba68bb293c2 --- /dev/null +++ b/airflow/ui/src/components/PipelineBreadcrumb.tsx @@ -0,0 +1,93 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { Link } from 'react-router-dom'; +import { + Box, Flex, Heading, useColorModeValue, +} from '@chakra-ui/react'; + +import type { + Dag as DagType, + DagRun as DagRunType, + Task as TaskType, +} from 'interfaces'; + +interface Props { + dagId: DagType['dagId']; + dagRunId?: DagRunType['dagRunId']; + taskId?: TaskType['taskId']; +} + +const PipelineBreadcrumb: React.FC = ({ dagId, dagRunId, taskId }) => { + const dividerColor = useColorModeValue('gray.100', 'gray.700'); + + return ( + + + PIPELINE + + {!dagRunId && dagId} + {dagRunId && ( + + {dagId} + + )} + + + {dagRunId && ( + <> + / + + RUN + + {!taskId && dagRunId} + {taskId && ( + + {dagRunId} + + )} + + + + )} + {taskId && ( + <> + / + + TASK INSTANCE + {taskId} + + + )} + + ); +}; + +export default PipelineBreadcrumb; diff --git a/airflow/ui/src/components/SectionNav.tsx b/airflow/ui/src/components/SectionNav.tsx new file mode 100644 index 0000000000000..47ae594ff7cbe --- /dev/null +++ b/airflow/ui/src/components/SectionNav.tsx @@ -0,0 +1,61 @@ +/*! + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { + Box, + useColorModeValue, +} from '@chakra-ui/react'; + +import SectionNavBtn from 'components/SectionNavBtn'; + +interface Props { + currentView: string; + navItems: { + label: string; + path: string; + }[] +} + +const SectionNav: React.FC = ({ currentView, navItems }) => { + const bg = useColorModeValue('gray.100', 'gray.700'); + return ( + + + + {navItems.map((item) => ( + + ))} + + + + ); +}; + +export default SectionNav; diff --git a/airflow/ui/src/components/SectionNavBtn.tsx b/airflow/ui/src/components/SectionNavBtn.tsx index b40695ba98e1a..16ab65f70ef43 100644 --- a/airflow/ui/src/components/SectionNavBtn.tsx +++ b/airflow/ui/src/components/SectionNavBtn.tsx @@ -26,16 +26,16 @@ interface Props { label: string; path: string; }; - currentLabel: string; + currentView: string; } -const SectionNavBtn: React.FC = ({ item, currentLabel }) => { +const SectionNavBtn: React.FC = ({ item, currentView }) => { const { label, path } = item; return (