Skip to content

[#57688] Harmonize Backlogs module with Primer#21723

Open
myabc wants to merge 57 commits intodevfrom
feature/57688-primerize-backlogs-classic
Open

[#57688] Harmonize Backlogs module with Primer#21723
myabc wants to merge 57 commits intodevfrom
feature/57688-primerize-backlogs-classic

Conversation

@myabc
Copy link
Contributor

@myabc myabc commented Jan 20, 2026

Important

This PR supersedes #21711 , which was an experiment based on Primer React

Ticket

https://community.openproject.org/wp/57688

What are you trying to accomplish?

This PR delivers a comprehensive visual and technical revamp of the Backlogs index page (Master Backlogs view) as part of our ongoing migration to the Primer design system.

Summary of Changes

🎯 Backlogs index page — Complete Rewrite

The main focus of this PR is modernizing the Backlogs index page (/projects/:project_id/backlogs):

New ViewComponent Architecture

  • Rewrote the entire frontend using 7 new ViewComponents:

    • BacklogComponent — Main container for each sprint/backlog
    • BacklogHeaderComponent — Collapsible header with sprint info and actions
    • BacklogMenuComponent — Sprint-level action menu
    • StoryComponent — Individual story row with drag handle
    • StoryMenuComponent — Story-level action menu
    • CollapsibleComponent — Reusable collapsible wrapper (ℹ️ this is temporary - changes will be upstreamed into Primer::OpenProject::BorderBox::CollapsibleHeader)
    • SprintPageHeaderComponent — Page header for sprint views
  • Built on Primer's BorderBox component for consistent styling

  • Added comprehensive component specs for all new components

Modern CSS & Responsive Design

  • Refactored SASS using CSS Grid and Container Queries for responsive layouts
  • Works well on mobile with two-line header layout for sprint info
  • Added new focus and clickable styles for .Box-row elements (Primer overrides) (ℹ️ again this is temporary and should be upstreamed into PVC)

Improved Interactions

  • Drag handle moved to left edge for better UX
  • Chevron repositioned to right of counter
  • Added "Open details" icon next to the More menu
  • Improved drag-and-drop with clearer drop zones

📊 Burndown chart — Rewritten with Chart.js

  • Replaced legacy jquery.flot with modern Chart.js
  • New Angular BurndownChartComponent registered as custom element
  • Includes i18n support and dev mode debugging
  • Removed ~5,200 lines of vendored jquery.flot code

🧹 Cleanup & Technical Improvements

  • Removed unused jquery.flot dependency from package.json
  • Fixed N+1 queries and improved permission checks
  • Backend: Improved attribute assignment logic in SetAttributesService
  • Added new Stimulus controllers for backlog interactions

Screenshots

Backlogs index page

Before After
Screenshot 2026-02-05 at 15 50 58 Screenshot 2026-02-05 at 15 47 54
Screenshot 2026-02-05 at 15 51 12 Screenshot 2026-02-05 at 15 48 15
Screenshot 2026-02-05 at 15 51 43 Screenshot 2026-02-05 at 15 49 33
Screenshot 2026-02-05 at 15 57 41 Screenshot 2026-02-05 at 15 48 45

Burndown chart

Before After
Screenshot 2026-02-05 at 15 51 30 Screenshot 2026-02-05 at 15 49 11

Taskboard

Before After
Screenshot 2026-02-05 at 15 51 20 Screenshot 2026-02-05 at 15 48 59

Not configured page

Before After
Screenshot 2026-02-05 at 16 17 17 image

What approach did you choose and why?

Turbo Frames and Streams: we use both in this iteration. Streams make sense when we control the update and know which areas to "surgically" update (e.g. Sprint name and dates). Frames make sense when the updates happen via a completely different part of the codebase (e.g. details pane)

Chart.js over jquery.flot: jquery.flot is unmaintained and doesn't support modern responsive design. Chart.js is actively maintained, has better accessibility, and integrates well with our existing frontend stack.

CSS Grid + Container Queries: These modern CSS features allow components to adapt based on their container size rather than viewport size, which is essential for the Backlogs page where columns can vary in width.

What this PR doesn't do

Provide robust / "full" accessibility: this PR includes basic keyboard support, but does not implement all WAI recommendations.

What this PR won't do

  • Move frontend code under modules/backlogs.
  • Fix the following known issues:
    • Drag and drop does not work well on mobile (no ticket)
    • Drag handle is missing button semantics and ARIA labels (no ticket yet)
    • BUG OP #70291: Notifications center content missing padding when WP pane open
    • BUG OP #71351: Single Date Picker does not show placeholder text
  • Update/primerize project backlog settings: see [#70778] Primerize Backlogs Project Settings #21822 instead.

Merge checklist

  • Update controllers to call services rather than model methods directly (@dombesz)
  • Fix sorting Sprints multiple types enabled (@dombesz)
  • Address security-related feedback (scoping finds/queries) from @dombesz
  • Find robust solution for reloading main pane on updating WP
  • Visual changes
  • Added/updated tests
    • controller specs
    • component specs
    • fix broken feature specs
    • add feature specs for new functionality
  • Cleanup unused translations
  • Added/updated documentation in Lookbook (patterns, previews, etc)
  • Tested major browsers (Chrome, Firefox, Edge, ...)

@myabc myabc force-pushed the feature/57688-primerize-backlogs-classic branch 3 times, most recently from 0a230ad to 5282199 Compare January 22, 2026 00:17
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 23, 2026 18:17 Inactive
@myabc myabc force-pushed the feature/57688-primerize-backlogs-classic branch from 5282199 to b8fa4bb Compare January 23, 2026 19:36
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 23, 2026 19:37 Inactive
@myabc myabc force-pushed the feature/57688-primerize-backlogs-classic branch from b8fa4bb to 7a142b0 Compare January 24, 2026 23:46
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 24, 2026 23:46 Inactive
@myabc myabc force-pushed the feature/57688-primerize-backlogs-classic branch 2 times, most recently from e1c2623 to 9e9c053 Compare January 24, 2026 23:55
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 24, 2026 23:56 Inactive
@myabc myabc force-pushed the feature/57688-primerize-backlogs-classic branch from 9e9c053 to 2777895 Compare January 25, 2026 00:30
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 25, 2026 00:31 Inactive
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 25, 2026 00:49 Inactive
@myabc myabc force-pushed the feature/57688-primerize-backlogs-classic branch from bebf10e to 25e2c19 Compare January 25, 2026 07:46
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 25, 2026 07:47 Inactive
@myabc myabc added feature javascript Pull requests that update Javascript code ruby Pull requests that update Ruby code labels Jan 27, 2026
@myabc myabc force-pushed the feature/57688-primerize-backlogs-classic branch from 25e2c19 to 7c350e7 Compare January 27, 2026 21:54
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 27, 2026 21:55 Inactive
Copy link
Contributor

@ulferts ulferts left a comment

Choose a reason for hiding this comment

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

Hey @myabc I really like where this is heading. Doesn't look like Y2K any more.

I didn't do a complete and thorough review but rather read through it all and remarked on things here and there.

There are a number of general topics I'd like to raise to discuss later:

  • Currently, the implementation is based on turbo streams. Backlogs are loaded individually when they are changed which, even with the limited set of scenarios provided does not always work and leads to complicated code. I have never done it and don't know if it will work but I would be interested in your opinion on using turbo drive or frame to load the whole page. To avoid undesired flickering and state changes, morphing would need to be used and the state would need to be sent to the backend (or morphing disabled on some parts of the application via JS). This would greatly simplify the backend/frontend communication I believe. Reloading the page upon changes in the split view e.g. would not need a lot of knowledge on what was changed. With the current approach, on a change to version, two backlogs would need to be reloaded. For the move action, this already seems to be failing and that is only within the stimulus controlled parts of the application. E.g. when moving the last story out of a backlog, it receives a way to big "Sprint backlog is empty" skeleton. All the other empty sprints are updated as well. This can be fixed but I see that as a warning sign of things to come:
Image
  • As mentioned above, changes in the work package split view are not reflected in the backlog view. Additionally, one can change the work package in a way that it will disappear from the page. Currently this can be done by changing the type to a non story type or by changing the version to one that is not on the page. The first problem will likely disappear once the setting is removed.
  • With the split view opening, there is very little space

Then there are some bugs I noticed when trying out the changes:

  • Moving the a story to the backlog and trying to move it again right away does not work
  • When opening the split view, the backlogs area sometimes did not allow to scroll horizontally. That happens after drag and drop, maybe?
  • Opening the split view breaks the right hand side margin so backlog and button are glued to the right hand side:
Image
  • We might consider opening a folded backlog when dragging. At least after hovering over it for some time
  • The menu item says "Edit title" when in fact I can edit more attributes:
Image

} else {
this.openSplitPane();
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

If the approach is kept, this part of the code needs to be dry-ed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ulferts I'm not 100% sure how to DRY this code. We might be able to make this into a more generic ListItemController if/when we extract BorderBoxList (see OP # 71402). / @HDinger

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ulferts I suppose we could look at moving the openSplitPane/openFullPane methods to the BacklogsController. Since StoryController defines an idValue, there is probably no need for the data-backlogs--story-split-url and data-backlogs--story-full-url attributes.

blankslate.with_heading(tag: :h4).with_content(t(".product_backlog.blank_slate_title"))
blankslate.with_description_content(t(".product_backlog.blank_slate_description"))
end
%>
Copy link
Contributor

Choose a reason for hiding this comment

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

Moving the logic to the component (backlog.sprint_backlog?) would simplify the template since the only difference seems to be in the I18n keys.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ulferts this should be addressed by #21879

mem
end
end

Copy link
Contributor

Choose a reason for hiding this comment

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

Some of these methods were used for performance optimizations. Without them, some n+1 queries are introduced:

Image

Eager loading status and type when the stories are loaded should fix that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ulferts ah, good catch.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ulferts can you check if this is still an issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ulferts this should be addressed by #21879

@myabc myabc force-pushed the feature/57688-primerize-backlogs-classic branch from 7c350e7 to e8486ab Compare January 29, 2026 17:54
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 29, 2026 17:55 Inactive
@myabc myabc force-pushed the feature/57688-primerize-backlogs-classic branch from e8486ab to e61b486 Compare January 29, 2026 18:04
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 January 29, 2026 18:05 Inactive
end
%>
<% else %>
<div class="op-backlogs-container" data-controller="generic-drag-and-drop">
Copy link
Contributor Author

Choose a reason for hiding this comment

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

FYI, the class and id naming is a bit inconsistent here. @HDinger and I picked these names while pairing. Better suggestions are welcome.

@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 February 5, 2026 20:10 Inactive
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 February 5, 2026 20:19 Inactive
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 February 5, 2026 20:41 Inactive
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 February 5, 2026 20:53 Inactive
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 February 6, 2026 11:33 Inactive
@dombesz dombesz force-pushed the feature/57688-primerize-backlogs-classic branch from 3fe8c0d to 3b3bdbd Compare February 6, 2026 12:48
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 February 6, 2026 12:48 Inactive
Copy link
Contributor

@HDinger HDinger left a comment

Choose a reason for hiding this comment

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

As disucssed, I just looked at the frontend part briefly. I guess there are some optimization with regards to responsivness that can be done, but those are no blockers. Further, also as discussed, I am no fan of the duplication of the collapsible header but it is fine to extract those changes in a separate step.

Additionally, I found some other things:

  • The breakpoint to switch the column layout in the editbale header should be earlier:
Image
  • When trying to set an end date to a verion, I ran into this error:
Image

@@ -0,0 +1,18 @@
<div class="position-relative mx-auto" style="height:60vh">
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not a big fan of using these classes directly. Primer already changed them in the past and the advantage of system_arguments is that we don't have to care about this. Further, inline styles are also kind of an anti-pattern for me.
So my suggestion would be, to add a class and put all those styles there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Primer's CSS is quite stable at this point, so I think it's ok to use utility classes directly. This was copy-pasted from the Budgets widget PR, so perhaps I can DRY that up once that is merged.

Comment on lines +156 to +162
.Box-row--focus-gray
&:focus-visible
background-color: var(--bgColor-muted)

.Box-row--focus-blue
&:focus-visible
background-color: var(--bgColor-accent-muted)
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't quite see why this is needed. The focus border already indicates the selection. If we need it, then it should be part of the PVC repo, shouldn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@HDinger This override could be upstreamed. Upstream currently uses .navigation-focus (which presumably is added with JavaScript?) rather than the newer :focus-visible pseudo-selector.

.Box-row--focus-gray {
  &.navigation-focus {
    background-color: var(--bgColor-muted);
  }
}

.Box-row--focus-blue {
  &.navigation-focus {
    background-color: var(--bgColor-accent-muted);
  }
}

Copy link
Contributor

@EinLama EinLama left a comment

Choose a reason for hiding this comment

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

Looks and feels so much better than the old backlog module 😁

I did not have a close look at styling or frontend details. For the controllers and general look and feel, I shall happily approve ✅

Please mind that I do not intend to overrule any open change requests by other people. This is just an approval for the code parts that are important to me.

myabc and others added 2 commits February 6, 2026 15:24
When only one of start/end date is set, show an en-dash
placeholder for the missing date instead of displaying
the single date alone (which looked like a milestone).

Remove .compact from date_range so nils flow through to
format_date_range, which now handles them explicitly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 February 6, 2026 18:25 Inactive
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 February 6, 2026 18:34 Inactive
myabc and others added 5 commits February 6, 2026 16:19
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract `update_header_component_via_turbo_stream` helper to remove
repeated backlog-build + turbo-stream-update block from `edit_name`,
`show_name`, and `update` actions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make it private (matching the parent class) and remove unnecessary
guards since params are always present in the routes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract `load_story` before_action and
`replace_backlog_component_via_turbo_stream` helper to remove
repeated story lookup and backlog component rendering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot temporarily deployed to gh-6899875-pr-21723 February 6, 2026 20:06 Inactive
We should generate friendly URLs using the project identifier, not
project id. While this inconsistency is pervasive in the OpenProject
codebase, this commit aims to at least make links within the backlogs
module a consistent format.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature javascript Pull requests that update Javascript code needs review pullpreview ruby Pull requests that update Ruby code

Development

Successfully merging this pull request may close these issues.

8 participants