Skip to content

Add Gantt chart view #51667

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft

Add Gantt chart view #51667

wants to merge 3 commits into from

Conversation

guan404ming
Copy link
Contributor

@guan404ming guan404ming commented Jun 12, 2025

Related Issue

#44672

cc @bbovenzi

Why

Our ui currently lack of gantt chart compared with AF2

How

Reimplement the gantt chart with chart.js

  • add chartjs-adapter-dayjs-4 to use dayjs to format time in chart.js
  • use grid data hook to fetch data and sync layout with grid view
  • add gantt tab in Run, TI and GroupTI page
Screen.Recording.2025-06-13.at.1.49.32.AM.mov

^ Add meaningful description above
Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named {pr_number}.significant.rst or {issue_number}.significant.rst, in airflow-core/newsfragments.

@guan404ming
Copy link
Contributor Author

guan404ming commented Jun 12, 2025

Known issue: the row alignment between grid view and gantt view would break when the HeaderCard height change. I would like to know that should we fix the height of HeaderCard to some constant or is there any better to solve this issue?

Thanks in advance!

@bbovenzi bbovenzi added this to the Airflow 3.1.0 milestone Jun 12, 2025
@bbovenzi
Copy link
Contributor

bbovenzi commented Jun 12, 2025

Very cool! I'll pull this down and play with it some more. But my quick thoughts:

  • Yes, let's make the header a fixed height. Ideally by using as a placeholder
  • I think we need to remove or at least speed up the animations in the gantt chart
  • Let's test this against a dag with 100+ tasks and see how the scrolling holds up

@guan404ming guan404ming marked this pull request as draft June 12, 2025 18:57
@jscheffl
Copy link
Contributor

Very cool!

@guan404ming guan404ming force-pushed the add-gantt branch 3 times, most recently from b82c025 to fb91fb7 Compare June 14, 2025 08:16
@guan404ming
Copy link
Contributor Author

guan404ming commented Jun 14, 2025

I've fixed the HeaderCard height for Gantt page and do some rwd implementation for Stat. Also, I've followed the official docs to optimize our Gantt. Current ui looks like

Screen.Recording.2025-06-14.at.4.25.38.PM.mov

@guan404ming guan404ming marked this pull request as ready for review June 14, 2025 08:35
@guan404ming guan404ming requested a review from Lee-W as a code owner June 14, 2025 08:35
Copy link
Member

@Lee-W Lee-W left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Taiwanese Mandarin translation looks good to me

</Flex>
<Stack direction={isGanttPage && containerWidth <= 380 ? "row" : "column"} mt={2} w="100%">
<HStack gap={1}>{actions}</HStack>
<StatsDisplay
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice call on moving it to an Info tooltip on small sizes.

return (
<Box>
<ActionButton
actionName="Show stats"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Translation needed

actionName="Show stats"
icon={<MdInfoOutline />}
onClick={() => setIsOpen(true)}
text="Stats"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too

export const HeaderCard = ({ actions, icon, isRefreshing, state, stats, subTitle, title }: Props) => {
const containerRef = useRef<HTMLDivElement>();
const containerWidth = useContainerWidth(containerRef);
const isGanttPage = useLocation().pathname.includes("/gantt");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this look when switching tabs? It might be better to stay consistent and always change the header based on containerWidth

<Dialog.Root lazyMount onOpenChange={() => setIsOpen(false)} open={isOpen} size="md">
<Dialog.Content backdrop>
<Dialog.Header bg="blue.muted">
<Heading size="xl">{title ?? "Statistics"}</Heading>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Translation needed here as well

text="Stats"
withText={false}
/>
<Dialog.Root lazyMount onOpenChange={() => setIsOpen(false)} open={isOpen} size="md">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a full dialog? I feel like a Popover is sufficient

@@ -72,7 +72,7 @@ const Instance = ({ dagId, isGroup, isMapped, runId, search, state, taskId }: Pr
<Link
replace
to={{
pathname: `/dags/${dagId}/runs/${runId}/tasks/${isGroup ? "group/" : ""}${taskId}${isMapped ? "/mapped" : ""}`,
pathname: `/dags/${dagId}/runs/${runId}/tasks/${isGroup ? "group/" : ""}${taskId}${isMapped ? "/mapped" : ""}/gantt`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's drop gantt from here or let's make sure we persist whatever the selected tab is

Comment on lines 77 to 78
{ icon: <MdOutlineTask />, label: "Task Instances", value: "" },
{ icon: <LuChartGantt />, label: "Gantt", value: "gantt" },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we forgot to include translations here.

const data = validTaskInstances.map((taskInstance) => ({
state: taskInstance.state,
x: [
dayjs(taskInstance.start_date).tz(selectedTimezone).format("YYYY-MM-DD HH:mm:ss.SSS"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to put the format as a const string at the top of the file or in datetimeUtils.ts? We repeat it a lot

@guan404ming guan404ming marked this pull request as draft June 16, 2025 08:46
@bbovenzi
Copy link
Contributor

I was thinking about the UX of this after #51764

Perhaps instead of putting the gantt in a tab on the right-hand panel, it should be an option to add onto the grid view on the left panel. Let's discuss first before you refactor everything though.

@guan404ming
Copy link
Contributor Author

I’m back!
I've noticed the change to the right panel. It seems like fixing the height of HeaderCard might not be ideal now. I agree with your idea of moving the Gantt chart to the left panel, similar to how we handle the grid and graph. That might be a better way to manage the Gantt chart UX without messing up the layout.

The only trade-off I see is that we might lose the shared scroll between the grid and the Gantt. What are your thoughts on that?

@bbovenzi
Copy link
Contributor

I’m back! I've noticed the change to the right panel. It seems like fixing the height of HeaderCard might not be ideal now. I agree with your idea of moving the Gantt chart to the left panel, similar to how we handle the grid and graph. That might be a better way to manage the Gantt chart UX without messing up the layout.

The only trade-off I see is that we might lose the shared scroll between the grid and the Gantt. What are your thoughts on that?

Either, the gantt chart can be a separate view on the left panel (easier)
Show/Hide Gantt , is an option on the grid view and they can live inside of the same container so the scrolling is easier to handle.

@guan404ming
Copy link
Contributor Author

I think the second idea is great and aligns better with the UX, especially since users will likely want to use both the grid and Gantt views at the same time. I’ll try implementing it first and see how it feels. My only concern is that it might get a bit cramped, so I might increase the min-width of the left pane to help with that. Thanks!

@bbovenzi
Copy link
Contributor

Yeah, when the gantt is shown we can change the minWidth!

@guan404ming guan404ming force-pushed the add-gantt branch 2 times, most recently from d6f2fef to b7b5ac6 Compare June 19, 2025 19:46
@guan404ming guan404ming force-pushed the add-gantt branch 2 times, most recently from ca08ea4 to 380ca4f Compare June 20, 2025 09:15
@guan404ming
Copy link
Contributor Author

Modification:

  • move gantt to grid and revert all changes about HeaderCard
  • add checkbox to determine whether to show the gantt chart
  • expand minWidth when gantt chart is enabled
  • extract time format func to utils
Screen.Recording.2025-06-20.at.5.12.21.PM.mov

@guan404ming guan404ming marked this pull request as ready for review June 20, 2025 09:18
@guan404ming guan404ming requested a review from jason810496 as a code owner June 20, 2025 09:18
Copy link
Member

@jason810496 jason810496 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM for the zh-TW translation and UI demo, but still need second eyes on the frontend code.

@jscheffl
Copy link
Contributor

Looks cool! Need to get used to have the Gantt on the left but from perspective of layout and usage this really makes sense! I slightly worry about conflict of space/pixels but maybe the amount of runs just need to be reduced.

Two small nits/glitches:
(1) It seems the calculation of the bar is not correct, I used the "integration test" DAG (Which I usually use for testing all sorts) and it seems while a task was running the start position was not correct:
image
("long running" is started after "my setup" as dependecy but bar starts before)

(2) The tooltips of execution duration is sometimes overlapping with the mouse, hiding the actualy bars it should provide more details to. Can these be positioned somewhere else (above/below)?
image

Otherwise I'd have said LGTM but these two glitches should be ironed-out.

@guan404ming
Copy link
Contributor Author

Thanks for pulling it down and testing! I'm also getting used to the new UI, but it's starting to make sense, having the Gantt on the left definitely feels more logical in terms of layout and usability.

  1. That bar rendering issue should be fixed now, it turns out formatDate needed a fallback to work properly in all cases.
image
  1. As for the tooltips, Chart.js automatically calculates the best position to avoid them being cut off or hidden. Fixing them to a static position can sometimes create problems. For example, if a tooltip is pinned to always appear above the bar, it might get clipped if the bar is already close to the top edge of the container.

I'm currently increasing the minWidth of the tooltip to help with the visual overlap issue. It seems to help a bit, but if there are any better ideas or suggestions, I’m definitely open to them!

Copy link
Contributor

@jscheffl jscheffl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the rework! Looks good to me.
Before merge would be good to have a review by one of the other peers as well. I would be good to merge it now.

Copy link
Member

@pierrejeambrun pierrejeambrun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue with having the Gantt next to the Grid is that I'm not sure we will have enough real eastate to cover all cases.

For instance if I start showing last 50 runs, or even last 100 runs, it's impossible to see the Gantt at the moment.

last 50 runs displayed (I could minimize the right panel to have more space)

Screenshot 2025-06-24 at 15 36 52

last 100 runs

Screenshot 2025-06-24 at 15 38 19

Also that leaves a big scroll. I don't know maybe a separate page at first would be much easier to handle all cases, and prevent enormous horizontal scroll. No strong opinion on this, you guys seemed all aligned on the "Gantt next to the Grid" so if you want to keep it like this it's fine

@jscheffl
Copy link
Contributor

Also that leaves a big scroll. I don't know maybe a separate page at first would be much easier to handle all cases, and prevent enormous horizontal scroll. No strong opinion on this, you guys seemed all aligned on the "Gantt next to the Grid" so if you want to keep it like this it's fine

I acknowledge that it is hard to use the Gantt in case you display ~50 or 100 runs. Maybe we should disable the Gantt by default if you pick more than 10 runs.

Separate page makes the usage harder I believe. And we had challenges aligning it on the right panel... but I like having it then you can directly select tasks that are long-runners and check logs or details.

@bbovenzi
Copy link
Contributor

bbovenzi commented Jun 25, 2025

  • the grid axes lines in dark mode should be fixed
  • selected color also should be the same as the grid view
  • we need a minimum width for tasks
  • We should only allow the grid view toggle button to show when the user has a dag run or task instance
  • We should try to prevent the graph from completely rerendering when changing selection between tasks on the same dag run
  • The spacing is off between task name, the grid and then the gap between the grid and graph views.
  • The translation seems broken from my end
  • We're missing the "crosshairs" hover effect
  • With one task the row height is not the same as the grid view
Screenshot 2025-06-25 at 2 36 35 PM Screenshot 2025-06-25 at 2 44 07 PM

We're close but I want to spend more time looking into the code to see if we can integrate the charting library into our view or if we'll need to go back to building our own gantt chart like in 2.x

@guan404ming
Copy link
Contributor Author

Thanks for the suggestions. They make sense to me and I would continue to polish the chart after my vacation til Saturday. I would also check the implementation is suitable or not for our current code base and would raise discussion here if there is any problems. Let me mark this as draft temporarily.

@guan404ming guan404ming marked this pull request as draft June 26, 2025 14:20
@guan404ming guan404ming marked this pull request as draft June 26, 2025 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:translations area:UI Related to UI/UX. For Frontend Developers. translation:default
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants