Skip to content

Commit

Permalink
Add data interval and next_dagrun_create_after info through tooltip t…
Browse files Browse the repository at this point in the history
…o next dagrun. (apache#43501)

* Add data interval to next dagrun.

* Disable react/jsx-boolean-value since false must be passed.

* Change tooltip to showTooltip. Fix font size to be same as last dag run.

* Add tooltip information for last dag run.

* Fix merge conflict.

* Fix PR comment regarding whitespace.
  • Loading branch information
tirkarthi authored Nov 5, 2024
1 parent ed3accb commit 9f6042c
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 83 deletions.
11 changes: 0 additions & 11 deletions airflow/ui/rules/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -439,17 +439,6 @@ export const reactRules = /** @type {const} @satisfies {FlatConfig.Config} */ ({
*/
[`${reactNamespace}/iframe-missing-sandbox`]: ERROR,

/**
* Enforce boolean attributes notation in JSX to never set it explicitly.
*
* @see [react/jsx-boolean-value](https://github.com/jsx-eslint/eslint-plugin-react/blob/HEAD/docs/rules/jsx-boolean-value.md)
*/
[`${reactNamespace}/jsx-boolean-value`]: [
ERROR,
"never",
{ assumeUndefinedIsFalse: true },
],

/**
* Enforce curly braces or braces in JSX props and/or children.
*
Expand Down
80 changes: 80 additions & 0 deletions airflow/ui/src/components/DagRunInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*!
* 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 { VStack, Text } from "@chakra-ui/react";
import dayjs from "dayjs";

import Time from "src/components/Time";
import { Tooltip } from "src/components/ui";

type Props = {
readonly dataIntervalEnd?: string | null;
readonly dataIntervalStart?: string | null;
readonly endDate?: string | null;
readonly logicalDate?: string | null;
readonly nextDagrunCreateAfter?: string | null;
readonly startDate?: string | null;
};

const DagRunInfo = ({
dataIntervalEnd,
dataIntervalStart,
endDate,
logicalDate,
nextDagrunCreateAfter,
startDate,
}: Props) =>
Boolean(dataIntervalStart) && Boolean(dataIntervalEnd) ? (
<Tooltip
content={
<VStack align="left" gap={0}>
<Text>
Data Interval Start: <Time datetime={dataIntervalStart} />
</Text>
<Text>
Data Interval End: <Time datetime={dataIntervalEnd} />
</Text>
{Boolean(nextDagrunCreateAfter) ? (
<Text>
Run After: <Time datetime={nextDagrunCreateAfter} />
</Text>
) : undefined}
{Boolean(logicalDate) ? (
<Text>Logical Date: {logicalDate}</Text>
) : undefined}
{Boolean(startDate) ? (
<Text>Start Date: {startDate}</Text>
) : undefined}
{Boolean(endDate) ? <Text>End Date: {endDate}</Text> : undefined}
{Boolean(startDate) && Boolean(endDate) ? (
<Text>
Duration:{" "}
{dayjs.duration(dayjs(endDate).diff(startDate)).asSeconds()}s
</Text>
) : undefined}
</VStack>
}
showArrow
>
<Text fontSize="sm">
<Time datetime={dataIntervalStart} showTooltip={false} />
</Text>
</Tooltip>
) : undefined;

export default DagRunInfo;
13 changes: 11 additions & 2 deletions airflow/ui/src/components/Time.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,14 @@ dayjs.extend(advancedFormat);
type Props = {
readonly datetime?: string | null;
readonly format?: string;
readonly showTooltip?: boolean;
};

const Time = ({ datetime, format = defaultFormat }: Props) => {
const Time = ({
datetime,
format = defaultFormat,
showTooltip = true,
}: Props) => {
const { selectedTimezone } = useTimezone();
const time = dayjs(datetime);

Expand All @@ -51,7 +56,11 @@ const Time = ({ datetime, format = defaultFormat }: Props) => {
<time
dateTime={datetime}
// show title if date is not UTC
title={selectedTimezone.toUpperCase() === "UTC" ? undefined : utcTime}
title={
selectedTimezone.toUpperCase() !== "UTC" && showTooltip
? utcTime
: undefined
}
>
{formattedTime}
</time>
Expand Down
24 changes: 16 additions & 8 deletions airflow/ui/src/pages/DagsList/Dag/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ import { FiCalendar, FiPlay } from "react-icons/fi";

import type { DAGResponse, DAGRunResponse } from "openapi/requests/types.gen";
import { DagIcon } from "src/assets/DagIcon";
import Time from "src/components/Time";
import DagRunInfo from "src/components/DagRunInfo";
import { TogglePause } from "src/components/TogglePause";
import { Tooltip } from "src/components/ui";

import { DagTags } from "../DagTags";
import { LatestRun } from "../LatestRun";

export const Header = ({
dag,
Expand Down Expand Up @@ -81,17 +80,26 @@ export const Header = ({
<Heading color="fg.muted" fontSize="xs">
Last Run
</Heading>
<LatestRun latestRun={latestRun} />
<LatestRun />
{Boolean(latestRun) && latestRun !== undefined ? (
<DagRunInfo
dataIntervalEnd={latestRun.data_interval_end}
dataIntervalStart={latestRun.data_interval_start}
endDate={latestRun.end_date}
logicalDate={latestRun.logical_date}
startDate={latestRun.start_date}
/>
) : undefined}
</VStack>
<VStack align="flex-start" gap={1}>
<Heading color="fg.muted" fontSize="xs">
Next Run
</Heading>
{Boolean(dag?.next_dagrun) ? (
<Text fontSize="sm">
<Time datetime={dag?.next_dagrun} />
</Text>
{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>
<div />
Expand Down
129 changes: 73 additions & 56 deletions airflow/ui/src/pages/DagsList/DagCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,71 +28,88 @@ import {
import { Link as RouterLink } from "react-router-dom";

import type { DAGWithLatestDagRunsResponse } from "openapi/requests/types.gen";
import Time from "src/components/Time";
import DagRunInfo from "src/components/DagRunInfo";
import { TogglePause } from "src/components/TogglePause";
import { Tooltip } from "src/components/ui";

import { DagTags } from "./DagTags";
import { LatestRun } from "./LatestRun";
import { RecentRuns } from "./RecentRuns";
import { Schedule } from "./Schedule";

type Props = {
readonly dag: DAGWithLatestDagRunsResponse;
};

export const DagCard = ({ dag }: Props) => (
<Box
borderColor="border.emphasized"
borderRadius={8}
borderWidth={1}
overflow="hidden"
>
<Flex
alignItems="center"
bg="bg.muted"
justifyContent="space-between"
px={3}
py={2}
export const DagCard = ({ dag }: Props) => {
const [latestRun] = dag.latest_dag_runs;

return (
<Box
borderColor="border.emphasized"
borderRadius={8}
borderWidth={1}
overflow="hidden"
>
<HStack>
<Tooltip
content={dag.description}
disabled={!Boolean(dag.description)}
showArrow
>
<Link asChild color="fg.info" fontWeight="bold">
<RouterLink to={`/dags/${dag.dag_id}`}>
{dag.dag_display_name}
</RouterLink>
</Link>
</Tooltip>
<DagTags tags={dag.tags} />
</HStack>
<HStack>
<TogglePause dagId={dag.dag_id} isPaused={dag.is_paused} />
</HStack>
</Flex>
<SimpleGrid columns={4} gap={4} height={20} px={3} py={2}>
<VStack align="flex-start" gap={1}>
<Heading color="gray.500" fontSize="xs">
Schedule
</Heading>
<Schedule dag={dag} />
</VStack>
<VStack align="flex-start" gap={1}>
<Heading color="gray.500" fontSize="xs">
Latest Run
</Heading>
<LatestRun latestRun={dag.latest_dag_runs[0]} />
</VStack>
<VStack align="flex-start" gap={1}>
<Heading color="gray.500" fontSize="xs">
Next Run
</Heading>
<Time datetime={dag.next_dagrun} />
</VStack>
<RecentRuns latestRuns={dag.latest_dag_runs} />
</SimpleGrid>
</Box>
);
<Flex
alignItems="center"
bg="bg.muted"
justifyContent="space-between"
px={3}
py={2}
>
<HStack>
<Tooltip
content={dag.description}
disabled={!Boolean(dag.description)}
showArrow
>
<Link asChild color="fg.info" fontWeight="bold">
<RouterLink to={`/dags/${dag.dag_id}`}>
{dag.dag_display_name}
</RouterLink>
</Link>
</Tooltip>
<DagTags tags={dag.tags} />
</HStack>
<HStack>
<TogglePause dagId={dag.dag_id} isPaused={dag.is_paused} />
</HStack>
</Flex>
<SimpleGrid columns={4} gap={4} height={20} px={3} py={2}>
<VStack align="flex-start" gap={1}>
<Heading color="gray.500" fontSize="xs">
Schedule
</Heading>
<Schedule dag={dag} />
</VStack>
<VStack align="flex-start" gap={1}>
<Heading color="gray.500" fontSize="xs">
Latest Run
</Heading>
{latestRun ? (
<DagRunInfo
dataIntervalEnd={latestRun.data_interval_end}
dataIntervalStart={latestRun.data_interval_start}
endDate={latestRun.end_date}
logicalDate={latestRun.logical_date}
startDate={latestRun.start_date}
/>
) : undefined}
</VStack>
<VStack align="flex-start" gap={1}>
<Heading color="gray.500" fontSize="xs">
Next Run
</Heading>
{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>
<RecentRuns latestRuns={dag.latest_dag_runs} />
</SimpleGrid>
</Box>
);
};
22 changes: 16 additions & 6 deletions airflow/ui/src/pages/DagsList/DagsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ import type {
DagRunState,
DAGWithLatestDagRunsResponse,
} from "openapi/requests/types.gen";
import DagRunInfo from "src/components/DagRunInfo";
import { DataTable } from "src/components/DataTable";
import { ToggleTableDisplay } from "src/components/DataTable/ToggleTableDisplay";
import type { CardDef } from "src/components/DataTable/types";
import { useTableURLState } from "src/components/DataTable/useTableUrlState";
import { ErrorAlert } from "src/components/ErrorAlert";
import { SearchBar } from "src/components/SearchBar";
import Time from "src/components/Time";
import { TogglePause } from "src/components/TogglePause";
import { Select } from "src/components/ui";
import {
Expand All @@ -53,7 +53,6 @@ import { pluralize } from "src/utils";
import { DagCard } from "./DagCard";
import { DagTags } from "./DagTags";
import { DagsFilters } from "./DagsFilters";
import { LatestRun } from "./LatestRun";
import { Schedule } from "./Schedule";

const columns: Array<ColumnDef<DAGWithLatestDagRunsResponse>> = [
Expand Down Expand Up @@ -89,16 +88,27 @@ const columns: Array<ColumnDef<DAGWithLatestDagRunsResponse>> = [
accessorKey: "next_dagrun",
cell: ({ row: { original } }) =>
Boolean(original.next_dagrun) ? (
<Time datetime={original.next_dagrun} />
<DagRunInfo
dataIntervalEnd={original.next_dagrun_data_interval_end}
dataIntervalStart={original.next_dagrun_data_interval_start}
nextDagrunCreateAfter={original.next_dagrun_create_after}
/>
) : undefined,
enableSorting: false,
header: "Next Dag Run",
},
{
accessorKey: "latest_dag_runs",
cell: ({ row: { original } }) => (
<LatestRun latestRun={original.latest_dag_runs[0]} />
),
cell: ({ row: { original } }) =>
original.latest_dag_runs[0] ? (
<DagRunInfo
dataIntervalEnd={original.latest_dag_runs[0].data_interval_end}
dataIntervalStart={original.latest_dag_runs[0].data_interval_start}
endDate={original.latest_dag_runs[0].end_date}
logicalDate={original.latest_dag_runs[0].logical_date}
startDate={original.latest_dag_runs[0].start_date}
/>
) : undefined,
enableSorting: false,
header: "Last Dag Run",
},
Expand Down

0 comments on commit 9f6042c

Please sign in to comment.