Skip to content

Commit

Permalink
Add basic Dag Run Details page
Browse files Browse the repository at this point in the history
  • Loading branch information
bbovenzi committed Dec 4, 2024
1 parent 99130ab commit a0d2d9c
Show file tree
Hide file tree
Showing 17 changed files with 604 additions and 116 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { Heading } from "@chakra-ui/react";
import type { DAGResponse } from "openapi/requests/types.gen";
import { Dialog } from "src/components/ui";

import { Graph } from "./Graph";
import { Graph } from "../pages/DagsList/Dag/Graph";

type TriggerDAGModalProps = {
dagDisplayName?: DAGResponse["dag_display_name"];
Expand Down
57 changes: 57 additions & 0 deletions airflow/ui/src/components/NavTabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*!
* 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 { Center, Flex } from "@chakra-ui/react";
import type { ReactNode } from "react";
import { NavLink } from "react-router-dom";

type Props = {
readonly rightButtons?: ReactNode;
readonly tabs: Array<{ label: string; value: string }>;
};

export const NavTabs = ({ rightButtons, tabs }: Props) => (
<Flex
alignItems="center"
borderBottomWidth={1}
justifyContent="space-between"
>
<Flex>
{tabs.map(({ label, value }) => (
<NavLink end key={value} to={value}>
{({ isActive }) => (
<Center
borderBottomColor="border.info"
borderBottomWidth={isActive ? 3 : 0}
color={isActive ? "fg" : "fg.muted"}
fontWeight="bold"
height="40px"
mb="-2px" // Show the border on top of its parent's border
pb={isActive ? 0 : "3px"}
px={4}
transition="all 0.2s ease"
>
{label}
</Center>
)}
</NavLink>
))}
</Flex>
<Flex alignSelf="flex-end">{rightButtons}</Flex>
</Flex>
);
33 changes: 33 additions & 0 deletions airflow/ui/src/components/Stat.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*!
* 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 { Heading, VStack } from "@chakra-ui/react";
import type { PropsWithChildren } from "react";

type Props = {
readonly label: string;
} & PropsWithChildren;

export const Stat = ({ children, label }: Props) => (
<VStack align="flex-start" gap={1}>
<Heading color="fg.muted" fontSize="xs">
{label}
</Heading>
{children}
</VStack>
);
55 changes: 55 additions & 0 deletions airflow/ui/src/components/ui/Breadcrumb/Root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*!
* 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 { Breadcrumb, type SystemStyleObject } from "@chakra-ui/react";
import React from "react";

export type BreadcrumbRootProps = {
separator?: React.ReactNode;
separatorGap?: SystemStyleObject["gap"];
} & Breadcrumb.RootProps;

export const Root = React.forwardRef<HTMLDivElement, BreadcrumbRootProps>(
(props, ref) => {
const { children, separator, separatorGap, ...rest } = props;

const validChildren = React.Children.toArray(children).filter(
React.isValidElement,
);

return (
<Breadcrumb.Root ref={ref} {...rest}>
<Breadcrumb.List gap={separatorGap}>
{validChildren.map((child, index) => {
const last = index === validChildren.length - 1;

return (
// eslint-disable-next-line react/no-array-index-key
<React.Fragment key={index}>
<Breadcrumb.Item>{child}</Breadcrumb.Item>
{!last && (
<Breadcrumb.Separator>{separator}</Breadcrumb.Separator>
)}
</React.Fragment>
);
})}
</Breadcrumb.List>
</Breadcrumb.Root>
);
},
);
26 changes: 26 additions & 0 deletions airflow/ui/src/components/ui/Breadcrumb/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*!
* 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 { Breadcrumb as ChakraBreadcrumb } from "@chakra-ui/react";

import { Root } from "./Root";

export const Breadcrumb = {
...ChakraBreadcrumb,
Root,
};
1 change: 1 addition & 0 deletions airflow/ui/src/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ export * from "./ProgressBar";
export * from "./Menu";
export * from "./Accordion";
export * from "./Status";
export * from "./Breadcrumb";
32 changes: 8 additions & 24 deletions airflow/ui/src/pages/DagsList/Dag/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
Box,
Flex,
Heading,
HStack,
SimpleGrid,
Text,
VStack,
} from "@chakra-ui/react";
import { Box, Flex, Heading, HStack, SimpleGrid, Text } from "@chakra-ui/react";
import { FiCalendar } from "react-icons/fi";

import type { DAGResponse, DAGRunResponse } from "openapi/requests/types.gen";
import { DagIcon } from "src/assets/DagIcon";
import DagRunInfo from "src/components/DagRunInfo";
import { Stat } from "src/components/Stat";
import { TogglePause } from "src/components/TogglePause";
import TriggerDAGTextButton from "src/components/TriggerDag/TriggerDAGTextButton";
import { Tooltip } from "src/components/ui";
Expand Down Expand Up @@ -62,10 +55,7 @@ export const Header = ({
<Flex>{dag ? <TriggerDAGTextButton dag={dag} /> : undefined}</Flex>
</Flex>
<SimpleGrid columns={4} gap={4} my={2}>
<VStack align="flex-start" gap={1}>
<Heading color="fg.muted" fontSize="xs">
Schedule
</Heading>
<Stat label="Schedule">
{Boolean(dag?.timetable_summary) ? (
<Tooltip content={dag?.timetable_description} showArrow>
<Text fontSize="sm">
Expand All @@ -74,11 +64,8 @@ export const Header = ({
</Text>
</Tooltip>
) : undefined}
</VStack>
<VStack align="flex-start" gap={1}>
<Heading color="fg.muted" fontSize="xs">
Last Run
</Heading>
</Stat>
<Stat label="Last Run">
{Boolean(latestRun) && latestRun !== undefined ? (
<DagRunInfo
dataIntervalEnd={latestRun.data_interval_end}
Expand All @@ -88,19 +75,16 @@ export const Header = ({
state={latestRun.state}
/>
) : undefined}
</VStack>
<VStack align="flex-start" gap={1}>
<Heading color="fg.muted" fontSize="xs">
Next Run
</Heading>
</Stat>
<Stat label="Next Run">
{Boolean(dag?.next_dagrun) && dag !== undefined ? (
<DagRunInfo
dataIntervalEnd={dag.next_dagrun_data_interval_end}
dataIntervalStart={dag.next_dagrun_data_interval_start}
nextDagrunCreateAfter={dag.next_dagrun_create_after}
/>
) : undefined}
</VStack>
</Stat>
<div />
<div />
</SimpleGrid>
Expand Down
51 changes: 16 additions & 35 deletions airflow/ui/src/pages/DagsList/Dag/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
import { Button, Center, Flex } from "@chakra-ui/react";
import { NavLink, useSearchParams } from "react-router-dom";
import { Button } from "@chakra-ui/react";
import { useSearchParams } from "react-router-dom";

import type { DAGResponse } from "openapi/requests/types.gen";
import { DagIcon } from "src/assets/DagIcon";
import { capitalize } from "src/utils";
import { DagVizModal } from "src/components/DagVizModal";
import { NavTabs } from "src/components/NavTabs";

import { DagVizModal } from "./DagVizModal";

const tabs = ["overview", "runs", "tasks", "events", "code"];
const tabs = [
{ label: "Overview", value: "" },
{ label: "Runs", value: "runs" },
{ label: "Tasks", value: "tasks" },
{ label: "Events", value: "events" },
{ label: "Code", value: "code" },
];

const MODAL = "modal";

Expand All @@ -47,39 +52,15 @@ export const DagTabs = ({ dag }: { readonly dag?: DAGResponse }) => {

return (
<>
<Flex
alignItems="center"
borderBottomWidth={1}
justifyContent="space-between"
>
<Flex>
{tabs.map((tab) => (
<NavLink end key={tab} to={tab === "overview" ? "" : tab}>
{({ isActive }) => (
<Center
borderBottomColor="border.info"
borderBottomWidth={isActive ? 3 : 0}
fontWeight={isActive ? "bold" : undefined}
height="40px"
mb="-2px" // Show the border on top of its parent's border
pb={isActive ? 0 : "3px"}
px={2}
transition="all 0.2s ease"
width="100px"
>
{capitalize(tab)}
</Center>
)}
</NavLink>
))}
</Flex>
<Flex alignSelf="flex-end">
<NavTabs
rightButtons={
<Button colorPalette="blue" onClick={onOpen} variant="ghost">
<DagIcon height={5} width={5} />
Graph
</Button>
</Flex>
</Flex>
}
tabs={tabs}
/>
<DagVizModal
dagDisplayName={dag?.dag_display_name}
dagId={dag?.dag_id}
Expand Down
32 changes: 8 additions & 24 deletions airflow/ui/src/pages/DagsList/DagCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
import {
Box,
Flex,
HStack,
Heading,
SimpleGrid,
VStack,
Link,
} from "@chakra-ui/react";
import { Box, Flex, HStack, SimpleGrid, Link } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";

import type { DAGWithLatestDagRunsResponse } from "openapi/requests/types.gen";
import DagRunInfo from "src/components/DagRunInfo";
import { Stat } from "src/components/Stat";
import { TogglePause } from "src/components/TogglePause";
import TriggerDAGIconButton from "src/components/TriggerDag/TriggerDAGIconButton";
import { Tooltip } from "src/components/ui";
Expand Down Expand Up @@ -82,16 +75,10 @@ export const DagCard = ({ dag }: Props) => {
</HStack>
</Flex>
<SimpleGrid columns={4} gap={4} height={20} px={3} py={2}>
<VStack align="flex-start" gap={1}>
<Heading color="fg.muted" fontSize="xs">
Schedule
</Heading>
<Stat label="Schedule">
<Schedule dag={dag} />
</VStack>
<VStack align="flex-start" gap={1}>
<Heading color="fg.muted" fontSize="xs">
Latest Run
</Heading>
</Stat>
<Stat label="Latest Run">
{latestRun ? (
<DagRunInfo
dataIntervalEnd={latestRun.data_interval_end}
Expand All @@ -101,19 +88,16 @@ export const DagCard = ({ dag }: Props) => {
state={latestRun.state}
/>
) : undefined}
</VStack>
<VStack align="flex-start" gap={1}>
<Heading color="fg.muted" fontSize="xs">
Next Run
</Heading>
</Stat>
<Stat label="Next Run">
{Boolean(dag.next_dagrun) ? (
<DagRunInfo
dataIntervalEnd={dag.next_dagrun_data_interval_end}
dataIntervalStart={dag.next_dagrun_data_interval_start}
nextDagrunCreateAfter={dag.next_dagrun_create_after}
/>
) : undefined}
</VStack>
</Stat>
<RecentRuns latestRuns={dag.latest_dag_runs} />
</SimpleGrid>
</Box>
Expand Down
Loading

0 comments on commit a0d2d9c

Please sign in to comment.