Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions airflow/ui/src/components/BreadcrumbStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*!
* 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 { HStack, Stat } from "@chakra-ui/react";
import type { ReactNode } from "react";
import { LiaSlashSolid } from "react-icons/lia";
import { Link as RouterLink } from "react-router-dom";

import { Breadcrumb } from "src/components/ui";

type Links = Array<{ label: ReactNode | string; labelExtra?: ReactNode; title?: string; value?: string }>;

export const BreadcrumbStats = ({ links }: { readonly links: Links }) => (
<Breadcrumb.Root separator={<LiaSlashSolid />}>
{links.map((link, index) => (
// eslint-disable-next-line react/no-array-index-key
<Stat.Root gap={0} key={`${link.title}-${index}`}>
<Stat.Label fontSize="xs" fontWeight="bold">
{link.title}
</Stat.Label>
<Stat.ValueText fontSize="sm" fontWeight="normal">
{index === links.length - 1 ? (
<Breadcrumb.CurrentLink>
<HStack>{link.label}</HStack>
</Breadcrumb.CurrentLink>
) : (
<Breadcrumb.Link asChild color="fg.info">
<HStack>
{link.labelExtra}
<RouterLink to={link.value ?? ""}>{link.label}</RouterLink>
</HStack>
</Breadcrumb.Link>
)}
</Stat.ValueText>
</Stat.Root>
))}
</Breadcrumb.Root>
);
50 changes: 50 additions & 0 deletions airflow/ui/src/components/Graph/AssetNode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*!
* 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 { Flex, Heading, HStack } from "@chakra-ui/react";
import type { NodeProps, Node as NodeType } from "@xyflow/react";
import { FiDatabase } from "react-icons/fi";

import { NodeWrapper } from "./NodeWrapper";
import type { CustomNodeProps } from "./reactflowUtils";

export const AssetNode = ({
data: { height, isSelected, label, width },
}: NodeProps<NodeType<CustomNodeProps, "asset">>) => (
<NodeWrapper>
<Flex
bg="bg"
borderColor={isSelected ? "border.inverted" : "border"}
borderRadius={5}
borderWidth={isSelected ? 6 : 2}
cursor="default"
flexDirection="column"
height={`${height}px`}
px={3}
py={isSelected ? 0 : 1}
width={`${width}px`}
>
<HStack>
<Heading ml={-2} size="sm">
<FiDatabase />
</Heading>
{label}
</HStack>
</Flex>
</NodeWrapper>
);
62 changes: 62 additions & 0 deletions airflow/ui/src/components/Graph/DagNode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*!
* 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 { Flex, HStack, Link } from "@chakra-ui/react";
import type { NodeProps, Node as NodeType } from "@xyflow/react";
import { Link as RouterLink } from "react-router-dom";

import { useDagServiceGetDag } from "openapi/queries";
import { DagIcon } from "src/assets/DagIcon";
import { TogglePause } from "src/components/TogglePause";

import { NodeWrapper } from "./NodeWrapper";
import type { CustomNodeProps } from "./reactflowUtils";

export const DagNode = ({
data: { height, isSelected, label, width },
}: NodeProps<NodeType<CustomNodeProps, "dag">>) => {
const { data: dag } = useDagServiceGetDag({ dagId: label });

return (
<NodeWrapper>
<Flex
bg="bg"
borderRadius={5}
borderWidth={isSelected ? 6 : 2}
cursor="default"
flexDirection="column"
height={`${height}px`}
px={3}
py={isSelected ? 0 : 1}
width={`${width}px`}
>
<HStack alignItems="center" gap={1}>
<DagIcon />
<Link asChild color="fg.info" mb={2}>
<RouterLink to={`/dags/${dag?.dag_id}`}>{dag?.dag_display_name ?? label}</RouterLink>
</Link>
</HStack>
<TogglePause
dagId={dag?.dag_id ?? label}
disabled={!Boolean(dag)}
isPaused={dag?.is_paused ?? false}
/>
</Flex>
</NodeWrapper>
);
};
8 changes: 4 additions & 4 deletions airflow/ui/src/components/TogglePause.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ import {
import { useConfig } from "src/queries/useConfig";

import { ConfirmationModal } from "./ConfirmationModal";
import { Switch } from "./ui";
import { Switch, type SwitchProps } from "./ui";

type Props = {
readonly dagDisplayName?: string;
readonly dagId: string;
readonly isPaused: boolean;
readonly skipConfirm?: boolean;
};
} & SwitchProps;

export const TogglePause = ({ dagDisplayName, dagId, isPaused, skipConfirm }: Props) => {
export const TogglePause = ({ dagDisplayName, dagId, isPaused, skipConfirm, ...rest }: Props) => {
const queryClient = useQueryClient();
const { onClose, onOpen, open } = useDisclosure();

Expand Down Expand Up @@ -83,7 +83,7 @@ export const TogglePause = ({ dagDisplayName, dagId, isPaused, skipConfirm }: Pr

return (
<>
<Switch checked={!isPaused} colorPalette="blue" onCheckedChange={onChange} size="sm" />
<Switch checked={!isPaused} colorPalette="blue" onCheckedChange={onChange} size="sm" {...rest} />
<ConfirmationModal
header={`${isPaused ? "Unpause" : "Pause"} ${dagDisplayName ?? dagId}?`}
onConfirm={onToggle}
Expand Down
36 changes: 3 additions & 33 deletions airflow/ui/src/layouts/Details/DagBreadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,18 @@
* specific language governing permissions and limitations
* under the License.
*/
import { HStack, Stat } from "@chakra-ui/react";
import type { ReactNode } from "react";
import { LiaSlashSolid } from "react-icons/lia";
import { Link as RouterLink, useParams } from "react-router-dom";
import { useParams } from "react-router-dom";

import {
useDagRunServiceGetDagRun,
useDagServiceGetDagDetails,
useTaskServiceGetTask,
} from "openapi/queries";
import { BreadcrumbStats } from "src/components/BreadcrumbStats";
import { StateBadge } from "src/components/StateBadge";
import Time from "src/components/Time";
import { TogglePause } from "src/components/TogglePause";
import { Breadcrumb } from "src/components/ui";

export const DagBreadcrumb = () => {
const { dagId = "", mapIndex = "-1", runId, taskId } = useParams();
Expand Down Expand Up @@ -104,33 +102,5 @@ export const DagBreadcrumb = () => {
links.push({ label: mapIndex, title: "Map Index" });
}

return (
<Breadcrumb.Root separator={<LiaSlashSolid />}>
{links.map((link, index) => (
// eslint-disable-next-line react/no-array-index-key
<Stat.Root gap={0} key={`${link.title}-${index}`}>
<Stat.Label fontSize="xs" fontWeight="bold">
{link.title}
</Stat.Label>
<Stat.ValueText fontSize="sm" fontWeight="normal">
{index === links.length - 1 ? (
<Breadcrumb.CurrentLink>
<HStack>
{link.labelExtra}
{link.label}
</HStack>
</Breadcrumb.CurrentLink>
) : (
<Breadcrumb.Link asChild color="fg.info">
<HStack>
{link.labelExtra}
<RouterLink to={link.value ?? ""}>{link.label}</RouterLink>
</HStack>
</Breadcrumb.Link>
)}
</Stat.ValueText>
</Stat.Root>
))}
</Breadcrumb.Root>
);
return <BreadcrumbStats links={links} />;
};
11 changes: 5 additions & 6 deletions airflow/ui/src/layouts/Details/Graph/Graph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,16 @@ import "@xyflow/react/dist/style.css";
import { useParams } from "react-router-dom";

import { useGridServiceGridData, useStructureServiceStructureData } from "openapi/queries";
import Edge from "src/components/Graph/Edge";
import { JoinNode } from "src/components/Graph/JoinNode";
import { TaskNode } from "src/components/Graph/TaskNode";
import type { CustomNodeProps } from "src/components/Graph/reactflowUtils";
import { useGraphLayout } from "src/components/Graph/useGraphLayout";
import { useColorMode } from "src/context/colorMode";
import { useOpenGroups } from "src/context/openGroups";
import useSelectedVersion from "src/hooks/useSelectedVersion";
import { isStatePending, useAutoRefresh } from "src/utils";

import Edge from "./Edge";
import { JoinNode } from "./JoinNode";
import { TaskNode } from "./TaskNode";
import type { CustomNodeProps } from "./reactflowUtils";
import { useGraphLayout } from "./useGraphLayout";

const nodeColor = (
{ data: { depth, height, isOpen, taskInstance, width }, type }: ReactFlowNode<CustomNodeProps>,
evenColor?: string,
Expand Down
78 changes: 78 additions & 0 deletions airflow/ui/src/pages/Asset/Asset.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*!
* 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 { Box, HStack } from "@chakra-ui/react";
import { ReactFlowProvider } from "@xyflow/react";
import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
import { useParams } from "react-router-dom";

import { useAssetServiceGetAsset } from "openapi/queries";
import { BreadcrumbStats } from "src/components/BreadcrumbStats";
import { ProgressBar } from "src/components/ui";

import { AssetEvents } from "../AssetEvents";
import { AssetGraph } from "./AssetGraph";
import { Header } from "./Header";

export const Asset = () => {
const { assetId } = useParams();

const { data: asset, isLoading } = useAssetServiceGetAsset(
{ assetId: assetId === undefined ? 0 : parseInt(assetId, 10) },
undefined,
{
enabled: Boolean(assetId),
},
);

const links = [
{ label: "Assets", value: "/assets" },
{
label: asset?.name,
title: "Asset",
value: `/assets/${assetId}`,
},
];

return (
<ReactFlowProvider>
<HStack justifyContent="space-between">
<BreadcrumbStats links={links} />
</HStack>
<ProgressBar size="xs" visibility={Boolean(isLoading) ? "visible" : "hidden"} />
<Box flex={1} minH={0}>
<PanelGroup autoSaveId={assetId} direction="horizontal">
<Panel defaultSize={20} minSize={6}>
<Box height="100%" position="relative" pr={2}>
<AssetGraph asset={asset} />
</Box>
</Panel>
<PanelResizeHandle className="resize-handle">
<Box bg="fg.subtle" cursor="col-resize" h="100%" transition="background 0.2s" w={0.5} />
</PanelResizeHandle>
<Panel defaultSize={50} minSize={20}>
<Header asset={asset} />
<Box h="100%" overflow="auto" px={2}>
<AssetEvents />
</Box>
</Panel>
</PanelGroup>
</Box>
</ReactFlowProvider>
);
};
Loading