Skip to content

Commit

Permalink
Refactor into single component per file.
Browse files Browse the repository at this point in the history
  • Loading branch information
tirkarthi committed Nov 11, 2024
1 parent 38ab71b commit feb5141
Show file tree
Hide file tree
Showing 8 changed files with 361 additions and 246 deletions.
4 changes: 2 additions & 2 deletions airflow/ui/src/pages/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import { Box, Heading } from "@chakra-ui/react";

import { Health } from "./Health";
import { Metrics } from "./Metrics";
import { HistoricalMetrics } from "./HistoricalMetrics";

export const Dashboard = () => (
<Box>
Expand All @@ -28,7 +28,7 @@ export const Dashboard = () => (
<Health />
</Box>
<Box mt={5}>
<Metrics />
<HistoricalMetrics />
</Box>
</Box>
);
52 changes: 52 additions & 0 deletions airflow/ui/src/pages/Dashboard/HistoricalMetrics/DagRunMetrics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*!
* 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, Heading, HStack } from "@chakra-ui/react";
import type { DAGRunStates } from "openapi-gen/requests/types.gen";

import { MetricSection } from "./MetricSection";
import { MetricsBadge } from "./MetricsBadge";

type DagRunMetricsProps = {
readonly dagRunStates: DAGRunStates;
readonly total: number;
};

const DAGRUN_STATES: Array<keyof DAGRunStates> = [
"queued",
"running",
"success",
"failed",
];

export const DagRunMetrics = ({ dagRunStates, total }: DagRunMetricsProps) => (
<Box borderRadius={5} borderWidth={1} p={2}>
<HStack mb={2}>
<MetricsBadge color="blue.solid" runs={total} />
<Heading>Dag Runs</Heading>
</HStack>
{DAGRUN_STATES.map((state) => (
<MetricSection
key={state}
runs={dagRunStates[state]}
state={state}
total={total}
/>
))}
</Box>
);
127 changes: 127 additions & 0 deletions airflow/ui/src/pages/Dashboard/HistoricalMetrics/HistoricalMetrics.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*!
* 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,
VStack,
Text,
createListCollection,
type SelectValueChangeDetails,
} from "@chakra-ui/react";
import dayjs from "dayjs";
import { useState } from "react";
import { FiCalendar } from "react-icons/fi";

import { useDashboardServiceHistoricalMetrics } from "openapi/queries";
import { ErrorAlert } from "src/components/ErrorAlert";
import Time from "src/components/Time";
import { Select } from "src/components/ui";

import { DagRunMetrics } from "./DagRunMetrics";
import { TaskInstanceMetrics } from "./TaskInstanceMetrics";

const timeOptions = createListCollection({
items: [
{ label: "Last 1 hour", value: "1" },
{ label: "Last 8 hours", value: "8" },
{ label: "Last 12 hours", value: "12" },
{ label: "Last 24 hours", value: "24" },
],
});

export const HistoricalMetrics = () => {
const defaultHour = "8";
const now = dayjs();
const [startDate, setStartDate] = useState(
now.subtract(Number(defaultHour), "hour").toISOString(),
);
const [endDate, setEndDate] = useState(now.toISOString());

const { data, error, isLoading } = useDashboardServiceHistoricalMetrics({
endDate,
startDate,
});

const dagRunTotal = data
? Object.values(data.dag_run_states).reduce(
(partialSum, value) => partialSum + value,
0,
)
: 0;

const taskRunTotal = data
? Object.values(data.task_instance_states).reduce(
(partialSum, value) => partialSum + value,
0,
)
: 0;

const handleTimeChange = ({
value,
}: SelectValueChangeDetails<Array<string>>) => {
const cnow = dayjs();

setStartDate(cnow.subtract(Number(value[0]), "hour").toISOString());
setEndDate(cnow.toISOString());
};

return (
<Box width="100%">
<ErrorAlert error={error} />
<VStack alignItems="left" gap={2}>
<HStack>
<FiCalendar />
<Select.Root
collection={timeOptions}
data-testid="sort-by-time"
defaultValue={[defaultHour]}
onValueChange={handleTimeChange}
width="200px"
>
<Select.Trigger>
<Select.ValueText placeholder="Duration" />
</Select.Trigger>
<Select.Content>
{timeOptions.items.map((option) => (
<Select.Item item={option} key={option.value}>
{option.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
<Text>
<Time datetime={startDate} /> - <Time datetime={endDate} />
</Text>
</HStack>
{!isLoading && data !== undefined && (
<Box>
<DagRunMetrics
dagRunStates={data.dag_run_states}
total={dagRunTotal}
/>
<TaskInstanceMetrics
taskInstanceStates={data.task_instance_states}
total={taskRunTotal}
/>
</Box>
)}
</VStack>
</Box>
);
};
73 changes: 73 additions & 0 deletions airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*!
* 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, Flex, HStack, VStack, Text } from "@chakra-ui/react";

import { capitalize } from "src/utils";
import { stateColor } from "src/utils/stateColor";

import { MetricsBadge } from "./MetricsBadge";

const BAR_WIDTH = 100;
const BAR_HEIGHT = 5;

type MetricSectionProps = {
readonly runs: number;
readonly state: string;
readonly total: number;
};

export const MetricSection = ({ runs, state, total }: MetricSectionProps) => {
// Calculate the given state as a percentage of total and draw a bar
// in state's color with width as state's percentage and remaining width filed as gray
const statePercent = total === 0 ? 0 : ((runs / total) * 100).toFixed(2);
const stateWidth = total === 0 ? 0 : (runs / total) * BAR_WIDTH;
const remainingWidth = BAR_WIDTH - stateWidth;

return (
<VStack align="left" gap={1} mb={4} ml={0} pl={0}>
<Flex justify="space-between">
<HStack>
<MetricsBadge
color={stateColor[state as keyof typeof stateColor]}
runs={runs}
/>
<Text> {capitalize(state)} </Text>
</HStack>
<Text color="fg.muted"> {statePercent}% </Text>
</Flex>
<HStack gap={0} mt={2}>
<Box
bg={stateColor[state as keyof typeof stateColor]}
borderLeftRadius={5}
height={`${BAR_HEIGHT}px`}
minHeight={2}
width={`${stateWidth}%`}
/>
<Box
bg={stateColor.queued}
borderLeftRadius={runs === 0 ? 5 : 0} // When there are no states then have left radius too since this is the only bar displayed
borderRightRadius={5}
height={`${BAR_HEIGHT}px`}
minHeight={2}
width={`${remainingWidth}%`}
/>
</HStack>
</VStack>
);
};
30 changes: 30 additions & 0 deletions airflow/ui/src/pages/Dashboard/HistoricalMetrics/MetricsBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*!
* 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 { Badge } from "@chakra-ui/react";

type MetricBadgeProps = {
readonly color: string;
readonly runs: number;
};

export const MetricsBadge = ({ color, runs }: MetricBadgeProps) => (
<Badge bg={color} borderRadius={15} minWidth={10} px={5} py={2} size="lg">
{runs}
</Badge>
);
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 { Box, Heading, HStack } from "@chakra-ui/react";
import type { TaskInstanceStateCount } from "openapi-gen/requests/types.gen";

import { MetricSection } from "./MetricSection";
import { MetricsBadge } from "./MetricsBadge";

type TaskInstanceMetricsProps = {
readonly taskInstanceStates: TaskInstanceStateCount;
readonly total: number;
};

const TASK_STATES: Array<keyof TaskInstanceStateCount> = [
"queued",
"running",
"success",
"failed",
"skipped",
];

export const TaskInstanceMetrics = ({
taskInstanceStates,
total,
}: TaskInstanceMetricsProps) => (
<Box borderRadius={5} borderWidth={1} mt={2} p={2}>
<HStack mb={2}>
<MetricsBadge color="blue.solid" runs={total} />
<Heading>Task Instances</Heading>
</HStack>

{TASK_STATES.map((state) => (
<MetricSection
key={state}
runs={taskInstanceStates[state]}
state={state}
total={total}
/>
))}
</Box>
);
20 changes: 20 additions & 0 deletions airflow/ui/src/pages/Dashboard/HistoricalMetrics/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*!
* 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.
*/

export { HistoricalMetrics } from "./HistoricalMetrics";
Loading

0 comments on commit feb5141

Please sign in to comment.