Skip to content

Re-design UI - #749

Open
timja wants to merge 88 commits into
jenkinsci:masterfrom
timja:refine-ui
Open

Re-design UI#749
timja wants to merge 88 commits into
jenkinsci:masterfrom
timja:refine-ui

Conversation

@timja

@timja timja commented Apr 3, 2026

Copy link
Copy Markdown
Member

TODO:

  • Get upstream dialogs change merged in that this PR requires
  • Check opening of the dialog is done right, I think its being done in JS when it should be done by setting the right jelly values
  • Check UI performance when lots of roles
  • Check groups work properly, e.g. display name and searching by display name
  • Adapt for system read
  • Test system read end 2 end once all other changes done
  • Check role manager permission works
  • Update permission templates to jelly dialog from js dialog
  • Switch to setting-subpage
  • Decide on navigation, e.g. permissions templates from roles page, should each action be duplicated or something else, Markus' feedback about subcategories could be useful here so we use the sidepanel instead of app-bar
  • The entry page name, heading is current role assignment but the title is something different
  • Should there be Save/ apply buttons or just save after every change like it currently does or should there be edit button that pops up a dialog
  • In the add dialog its just got plain radio and checkboxes, should that be enhanced?
  • Bottom button bar not working, fixed on role assignments page
  • Security realm update user and group name e.g ID to display name
    • Should work now, need to test groups
  • Review code in general

For later:

  • Item based selection rather than just regex
  • User / group autocompletion / validation on add - might look at it in this
role-strategy.mov

Testing done

Extensively manually tested including with different personas:

  • Admin
  • SystemRead
  • Item roles admin
  • Agent roles admin
  • Item and Agent roles admin

Verified behaviour is correct across all of these.
I intend to create some playwright tests as well as there's 0 UI coverage at all in this repo (or in ATH), but that will likely be for another PR although I may stack it on this to get it going

Submitter checklist

  • Make sure you are opening from a topic/feature/bugfix branch (right side) and not your main branch!
  • Ensure that the pull request title represents the desired changelog entry
  • Please describe what you did
  • Link to relevant issues in GitHub or Jira
  • Link to relevant pull requests, esp. upstream and downstream changes
  • Ensure you have provided tests that demonstrate the feature works or the issue is fixed

@timja

timja commented Apr 4, 2026

Copy link
Copy Markdown
Member Author

There's probably some changes needed but its ready for a look from a design PoV.

  • Code not ready for review yet
  • Documentation probably needs updating
  • Once we're happy with the design, data saving etc, I can look at ATH / PCT.

cc @mawinter69

@timja timja changed the title Init Re-design UI Apr 5, 2026
Comment thread pom.xml Outdated
Comment thread pom.xml Outdated
Co-authored-by: Tim Jacomb <21194782+timja@users.noreply.github.com>
Comment thread seed-data.sh
@@ -0,0 +1,201 @@
#!/bin/bash

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Useful script for performance testing

@timja
timja marked this pull request as ready for review April 21, 2026 14:55
@timja
timja requested a review from a team as a code owner April 21, 2026 14:55

@mawinter69 mawinter69 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Check boxes in the dialogs are touching each other.

Image

Maybe add some gap

.rsp-assign-dialog__group {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

</div>
</f:entry>

<f:bottomButtonBar>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
<f:bottomButtonBar>
<f:bottomButtonBar borderless="true">

</div>
</f:entry>

<f:bottomButtonBar>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
<f:bottomButtonBar>
<f:bottomButtonBar borderless="true">


</f:entry>

<f:bottomButtonBar>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
<f:bottomButtonBar>
<f:bottomButtonBar borderless="true">


</f:entry>

<f:bottomButtonBar>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
<f:bottomButtonBar>
<f:bottomButtonBar borderless="true">

</div>
</f:entry>

<f:bottomButtonBar>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
<f:bottomButtonBar>
<f:bottomButtonBar borderless="true">

</div>
</f:entry>

<f:bottomButtonBar>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
<f:bottomButtonBar>
<f:bottomButtonBar borderless="true">

@mawinter69

Copy link
Copy Markdown
Contributor
  • When I assign a role and that user already has some roles assigned there is no error or warning shown, it seems to silently merge the roles. This is dangerous when when you have userids that are just like numbers, a simple typo could make you grant someone permissions that he should not have
  • In the role management page the actually used userid/groupid is not visible also not as tooltip
  • It would be nice when in the add role dialog the userid/groupis is directly resolved and the user/group displayname is shown, this helps to prevent giving the wrong user permissions
  • In the dialogs to assign roles it is hard to know which permissions are connected to the roles. A tooltip with the permissions might help here, either hovering the role or via a small icon on the right
  • Similarly when viewing the assigned roles of a user a tooltip might be helpful

@mawinter69

Copy link
Copy Markdown
Contributor

Seems in Matrix auth you have added small ? icons showing the description of the permission as tooltip. Would be good to have that here as well

@mawinter69

Copy link
Copy Markdown
Contributor

In Matrix auth the permissions are directly editable. Maybe we can do that here as well (unless a template is used) and limit the edit dialog to item and agent roles to modify the pattern / template only.

@mawinter69

Copy link
Copy Markdown
Contributor

Handling for ambiguous assignments seems to be missing

@mawinter69

Copy link
Copy Markdown
Contributor
  • the lib/rolestrategy/dropdownList.jelly taglib is not needed anymore
  • the braces.svg is not used anymore I think
  • Adding a new role or assignment is currently a page reload, I think in matrix auth you made that purely in javascript

@timja

timja commented Apr 25, 2026

Copy link
Copy Markdown
Member Author

Adding a new role or assignment is currently a page reload, I think in matrix auth you made that purely in javascript

Yes because saving is done when the security page as a whole is saved, whereas here its saved when the dialog closes.

It seemed clearer in this case to simplify everything by saving as part of the dialog, rather than saving every object on the page when you likely aren't changing much of it when adding e.g. new roles.

@mawinter69

Copy link
Copy Markdown
Contributor

Adding a new role or assignment is currently a page reload, I think in matrix auth you made that purely in javascript

Yes because saving is done when the security page as a whole is saved, whereas here its saved when the dialog closes.

It seemed clearer in this case to simplify everything by saving as part of the dialog, rather than saving every object on the page when you likely aren't changing much of it when adding e.g. new roles.

That would be an argument against my comment about being able to directly modify the permissions on the page.
The APIs should be there that allow to add the role/assignment in javascript in the background. Would just need rendering the new entry once adding/editing was successful. When you want to add several users you and have that page reload after every user is not the best UX I think.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 47 out of 49 changed files in this pull request and generated 7 comments.

Comment on lines +36 to +42
[<j:forEach var="role" items="${it.strategy.getGrantedRolesEntries(it.strategy.GLOBAL)}" varStatus="s"><j:if test="${!s.first}">,</j:if>{"name":"${h.jsStringEscape(role.key.name)}","permissions":[<j:forEach var="p" items="${role.key.permissions}" varStatus="ps"><j:if test="${!ps.first}">,</j:if>"${h.jsStringEscape(p.group.title.toString() + '/' + p.name)}"</j:forEach>]}</j:forEach>]
</script>
<script type="application/json" id="rsp-roles-project">
[<j:forEach var="role" items="${it.strategy.getGrantedRolesEntries(it.strategy.PROJECT)}" varStatus="s"><j:if test="${!s.first}">,</j:if>{"name":"${h.jsStringEscape(role.key.name)}","pattern":"${h.jsStringEscape(role.key.pattern.toString())}","permissions":[<j:forEach var="p" items="${role.key.permissions}" varStatus="ps"><j:if test="${!ps.first}">,</j:if>"${h.jsStringEscape(p.group.title.toString() + '/' + p.name)}"</j:forEach>]}</j:forEach>]
</script>
<script type="application/json" id="rsp-roles-slave">
[<j:forEach var="role" items="${it.strategy.getGrantedRolesEntries(it.strategy.SLAVE)}" varStatus="s"><j:if test="${!s.first}">,</j:if>{"name":"${h.jsStringEscape(role.key.name)}","pattern":"${h.jsStringEscape(role.key.pattern.toString())}","permissions":[<j:forEach var="p" items="${role.key.permissions}" varStatus="ps"><j:if test="${!ps.first}">,</j:if>"${h.jsStringEscape(p.group.title.toString() + '/' + p.name)}"</j:forEach>]}</j:forEach>]
<l:search-bar placeholder="${%Filter permissions}" clazz="rsp-perm-dialog-filter" hasKeyboardShortcut="false" />

<div class="rsp-assign-dialog__roles" name="permissions">
<j:forEach var="roleType" items="globalRoles,projectRoles,slaveRoles">
Comment thread seed-data.sh
Comment on lines +2 to +13
# Seed script: creates ~500 roles, ~150 agent roles, 2500 user assignments, 250 groups
# Usage: ./seed-data.sh [JENKINS_URL]
set -euo pipefail

JENKINS="${1:-http://localhost:8080/jenkins}"
API="$JENKINS/role-strategy/strategy"
JENKINS_USER="${JENKINS_USERNAME}"
JENKINS_TOKEN="${JENKINS_API_TOKEN}"

echo "=== Seeding role-strategy data at $JENKINS ==="
echo "Authenticating as $JENKINS_USER..."
curl -sf -u "$JENKINS_USER:$JENKINS_TOKEN" "$JENKINS/api/json" > /dev/null || { echo "ERROR: Cannot reach Jenkins"; exit 1; }
@@ -0,0 +1 @@
implied=implied
Comment on lines +462 to +499
Behaviour.specify(".rsp-template-edit", "RoleStrategyTemplates", 0, (btn) => {
if (btn.dataset.initialized === "true") return;
btn.dataset.initialized = "true";
btn.addEventListener("click", (e) => {
e.stopPropagation();
const initDialog = () => {
const form = document.querySelector("form[name='edit-template']");
if (!form) {
setTimeout(initDialog, 100);
return;
}
tplInitDialog(form, "rsp-edit-template-submit-btn");
};
setTimeout(initDialog, 200);
});
});

// Add template — dialog opened via data-type="dialog-opener"
Behaviour.specify(
".rsp-add-template-btn",
"RoleStrategyTemplates",
0,
(btn) => {
if (btn.dataset.initialized === "true") return;
btn.dataset.initialized = "true";
btn.addEventListener("click", () => {
const initDialog = () => {
const form = document.querySelector("form[name='add-template']");
if (!form) {
setTimeout(initDialog, 100);
return;
}
tplInitDialog(form, "rsp-add-template-submit-btn");
};
setTimeout(initDialog, 200);
});
},
);
Comment on lines +385 to +400
Behaviour.specify(
"#rsp-template-cards .rsp-perm__item input[type=checkbox]",
"RoleStrategyTemplates",
0,
(cb) => {
if (cb.dataset.initialized === "true") return;
cb.dataset.initialized = "true";
cb.addEventListener("change", () => {
const card = cb.closest(".rsp-card");
if (card) {
tplUpdateImplied(card);
tplUpdateSummary(card);
}
});
},
);
Comment on lines +464 to +474
@CheckForNull
private static JSONObject getSubmittedFormOrRedirect(
StaplerRequest2 req, StaplerResponse2 rsp, String redirectUrl) throws IOException {
req.setCharacterEncoding("UTF-8");
JSONObject json = req.getSubmittedForm();
JSONObject rolesMapping;
if (json.has("submit")) {
String rm = json.getString("rolesMapping");
rolesMapping = JSONObject.fromObject(rm);
} else {
rolesMapping = json.getJSONObject("rolesMapping");
try {
return req.getSubmittedForm();
} catch (Exception e) {
rsp.sendRedirect(redirectUrl);
return null;
}
}
@timja

timja commented May 17, 2026

Copy link
Copy Markdown
Member Author

@mawinter69 would you be open to this being done in React.

I've got a port here: timja#1
The state handling is far easier there, all the page edit, save etc.

Its got issues that I'm working through but just wanted to see if you'd be open to that?

@mawinter69

Copy link
Copy Markdown
Contributor

I have no experience with react, but if you think that this will make things easier then go ahead

@timja

timja commented May 18, 2026

Copy link
Copy Markdown
Member Author

I have no experience with react, but if you think that this will make things easier then go ahead

Yeah its far easier to handle the interactivity and simpler code.

I'll extract some changes from this and start opening some PRs, react port is mostly there.

This was referenced May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants