This is an automated email from the ASF dual-hosted git repository.
ephraimanierobi pushed a commit to branch v3-1-test
in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 620aa0c432429f3f67b37d33aa058d96f138e223
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Wed Jan 14 14:45:54 2026 -0500
[v3-1-test] UI: Update PoolBar to separate Scheduled and Deferred slots
(#59270) (#60538)
* UI: Update PoolBar to separate Scheduled and Deferred slots
* UI: Use standard tooltips and cleanup i18n keys
(cherry picked from commit 41f3dfec4937f9096c482fbc5d2a463d15050dd6)
Co-authored-by: Anshu Singh <[email protected]>
---
.../airflow/ui/public/i18n/locales/en/common.json | 10 +-
.../src/airflow/ui/src/components/PoolBar.tsx | 126 +++++++++++++--------
.../pages/Dashboard/PoolSummary/PoolSummary.tsx | 4 +-
.../src/airflow/ui/src/pages/Pools/PoolBarCard.tsx | 5 +-
4 files changed, 84 insertions(+), 61 deletions(-)
diff --git a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
index 3b9b1c71c33..0a59f203473 100644
--- a/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
+++ b/airflow-core/src/airflow/ui/public/i18n/locales/en/common.json
@@ -145,15 +145,6 @@
"placeholder": "Add a note...",
"taskInstance": "Task Instance Note"
},
- "pools": {
- "deferred": "Deferred",
- "open": "Open",
- "pools_one": "pool",
- "pools_other": "pools",
- "queued": "Queued",
- "running": "Running",
- "scheduled": "Scheduled"
- },
"reset": "Reset",
"runId": "Run ID",
"runTypes": {
@@ -192,6 +183,7 @@
"failed": "Failed",
"no_status": "No Status",
"none": "No Status",
+ "open": "Open",
"planned": "Planned",
"queued": "Queued",
"removed": "Removed",
diff --git a/airflow-core/src/airflow/ui/src/components/PoolBar.tsx
b/airflow-core/src/airflow/ui/src/components/PoolBar.tsx
index d7b68b6b560..e19cce8ecdd 100644
--- a/airflow-core/src/airflow/ui/src/components/PoolBar.tsx
+++ b/airflow-core/src/airflow/ui/src/components/PoolBar.tsx
@@ -16,11 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Flex, Link, Box } from "@chakra-ui/react";
+import { Box, Flex, Text, VStack, Link, HStack } from "@chakra-ui/react";
import { useTranslation } from "react-i18next";
import { Link as RouterLink } from "react-router-dom";
-import type { PoolResponse } from "openapi/requests/types.gen";
+import type { PoolResponse, TaskInstanceState } from
"openapi/requests/types.gen";
+import { StateIcon } from "src/components/StateIcon";
import { Tooltip } from "src/components/ui";
import { SearchParamsKeys } from "src/constants/searchParams";
import { type Slots, slotConfigs } from "src/utils/slots";
@@ -36,52 +37,87 @@ export const PoolBar = ({
}) => {
const { t: translate } = useTranslation("common");
+ const isDashboard = Boolean(poolsWithSlotType);
+ const includeDeferredInBar = "include_deferred" in pool &&
pool.include_deferred;
+ const barSlots = ["running", "queued", "open"];
+
+ if (isDashboard || includeDeferredInBar) {
+ barSlots.push("deferred");
+ }
+ const infoSlots = ["scheduled"];
+
+ if (!isDashboard && !includeDeferredInBar) {
+ infoSlots.push("deferred");
+ }
+
+ const preparedSlots = slotConfigs.map((config) => {
+ const slotType = config.key.replace("_slots", "") as TaskInstanceState;
+
+ return {
+ ...config,
+ label: translate(`common:states.${slotType}`),
+ slotType,
+ slotValue: (pool[config.key] as number | undefined) ?? 0,
+ };
+ });
+
return (
- <>
- {slotConfigs.map(({ color, icon, key }) => {
- const slotValue = pool[key];
- const flexValue = slotValue / totalSlots || 0;
+ <VStack align="stretch" gap={1} w="100%">
+ <Flex bg="bg.muted" borderRadius="md" h="20px" overflow="hidden"
w="100%">
+ {preparedSlots
+ .filter((slot) => barSlots.includes(slot.slotType) && slot.slotValue
> 0)
+ .map((slot) => {
+ const flexValue = slot.slotValue / totalSlots || 0;
- if (flexValue === 0) {
- return undefined;
- }
+ const poolContent = (
+ <Tooltip content={slot.label} key={slot.key} showArrow={true}>
+ <Flex
+ alignItems="center"
+ bg={`${slot.color}.solid`}
+ color={`${slot.color}.contrast`}
+ gap={1}
+ h="100%"
+ justifyContent="center"
+ overflow="hidden"
+ px={1}
+ w="100%"
+ >
+ {slot.icon}
+ <Text fontSize="xs" fontWeight="bold" truncate>
+ {slot.slotValue}
+ </Text>
+ </Flex>
+ </Tooltip>
+ );
- const slotType = key.replace("_slots", "");
- const poolCount = poolsWithSlotType ? poolsWithSlotType[key] : 0;
- const tooltipContent = `${translate(`pools.${slotType}`)}:
${slotValue} (${poolCount} ${translate("pools.pools", { count: poolCount })})`;
- const poolContent = (
- <Tooltip content={tooltipContent} key={key}>
- <Flex
- alignItems="center"
- bg={`${color}.solid`}
- color={`${color}.contrast`}
- gap={1}
- h="100%"
- justifyContent="center"
- px={1}
- textAlign="center"
- w="100%"
- >
- {icon}
- {slotValue}
- </Flex>
- </Tooltip>
- );
+ return slot.color !== "success" && "name" in pool ? (
+ <Link asChild flex={flexValue} key={slot.key}>
+ <RouterLink
+
to={`/task_instances?${SearchParamsKeys.STATE}=${slot.color}&${SearchParamsKeys.POOL}=${pool.name}`}
+ >
+ {poolContent}
+ </RouterLink>
+ </Link>
+ ) : (
+ <Box flex={flexValue} key={slot.key}>
+ {poolContent}
+ </Box>
+ );
+ })}
+ </Flex>
- return color !== "success" && "name" in pool ? (
- <Link asChild display="flex" flex={flexValue} key={key}>
- <RouterLink
-
to={`/task_instances?${SearchParamsKeys.STATE}=${color}&${SearchParamsKeys.POOL}=${pool.name}`}
- >
- {poolContent}
- </RouterLink>
- </Link>
- ) : (
- <Box display="flex" flex={flexValue} key={key}>
- {poolContent}
- </Box>
- );
- })}
- </>
+ <HStack gap={4} wrap="wrap">
+ {preparedSlots
+ .filter((slot) => infoSlots.includes(slot.slotType) &&
slot.slotValue > 0)
+ .map((slot) => (
+ <HStack gap={1} key={slot.key}>
+ <StateIcon size={12} state={slot.slotType} />
+ <Text color="fg.muted" fontSize="xs" fontWeight="medium">
+ {slot.label}: {slot.slotValue}
+ </Text>
+ </HStack>
+ ))}
+ </HStack>
+ </VStack>
);
};
diff --git
a/airflow-core/src/airflow/ui/src/pages/Dashboard/PoolSummary/PoolSummary.tsx
b/airflow-core/src/airflow/ui/src/pages/Dashboard/PoolSummary/PoolSummary.tsx
index be08ecb15c4..9fadd5bab2b 100644
---
a/airflow-core/src/airflow/ui/src/pages/Dashboard/PoolSummary/PoolSummary.tsx
+++
b/airflow-core/src/airflow/ui/src/pages/Dashboard/PoolSummary/PoolSummary.tsx
@@ -98,9 +98,7 @@ export const PoolSummary = () => {
{isLoading ? (
<Skeleton borderRadius="full" h={8} w="100%" />
) : (
- <Flex bg="bg" borderRadius="full" display="flex" overflow="hidden"
w="100%">
- <PoolBar pool={aggregatePool} poolsWithSlotType={poolsWithSlotType}
totalSlots={totalSlots} />
- </Flex>
+ <PoolBar pool={aggregatePool} poolsWithSlotType={poolsWithSlotType}
totalSlots={totalSlots} />
)}
</Box>
);
diff --git a/airflow-core/src/airflow/ui/src/pages/Pools/PoolBarCard.tsx
b/airflow-core/src/airflow/ui/src/pages/Pools/PoolBarCard.tsx
index d1d416e9c7e..218eb24a6b3 100644
--- a/airflow-core/src/airflow/ui/src/pages/Pools/PoolBarCard.tsx
+++ b/airflow-core/src/airflow/ui/src/pages/Pools/PoolBarCard.tsx
@@ -61,11 +61,8 @@ const PoolBarCard = ({ pool }: PoolBarCardProps) => {
)}
</VStack>
</Flex>
-
<Box margin={4}>
- <Flex bg="bg.muted" borderRadius="md" h="20px" overflow="hidden"
w="100%">
- <PoolBar pool={pool} totalSlots={pool.slots} />
- </Flex>
+ <PoolBar pool={pool} totalSlots={pool.slots} />
</Box>
</Box>
);