Skip to content

Use displayId in work package URLs and stateful navigation#22733

Open
akabiru wants to merge 16 commits intofeature/73717-adapt-work-package-lists-for-project-based-semantic-work-package-identifiersfrom
implementation/73797-use-displayid-in-work-package-urls
Open

Use displayId in work package URLs and stateful navigation#22733
akabiru wants to merge 16 commits intofeature/73717-adapt-work-package-lists-for-project-based-semantic-work-package-identifiersfrom
implementation/73797-use-displayid-in-work-package-urls

Conversation

@akabiru
Copy link
Copy Markdown
Member

@akabiru akabiru commented Apr 13, 2026

Ticket

https://community.openproject.org/wp/73797
https://community.openproject.org/wp/73834

Builds on #18997. Absorbs #22739 into a single PR.

What are you trying to accomplish?

When a project has semantic work package identifiers enabled, clicking any work package link still showed the numeric PK in the browser URL bar (e.g. /work_packages/42/activity). This changes frontend-generated URLs and stateful navigations to use the semantic displayId instead (e.g. /work_packages/PROJ-7/activity), giving users readable, shareable URLs that match the identifiers they see in the UI.

This PR mainly focuses on the work package list and how views- it's possible other parts are missed and will be handled separately.

What approach did you choose and why?

Semantic IDs (e.g. PROJ-7) should appear in URLs, but internal systems (selection, focus, caching) are keyed by numeric PK. This approach cleanly separates URL routing from internal identity.

Link elements now carry both data-work-package-id (numeric PK for selection) and data-routing-id (semantic ID for navigation). Click and navigation handlers use a shared resolveRoutingId() helper to look up the semantic ID from the states cache. Components that already have the WP resource in scope call .displayId directly.

Where route params flow into internal systems (split view focus, card highlight, bulk delete, WP cache), they're resolved back to numeric PKs so selection and caching remain stable. A shared formatWorkPackageId() utility replaces duplicated #-prefix formatting.

API-only paths (time entries, hover cards, modals) stay numeric since they never appear in the address bar.

Merge checklist

  • Added/updated tests
  • Tested major browsers (Chrome, Firefox, Edge, ...)

@akabiru akabiru marked this pull request as draft April 13, 2026 11:31
@akabiru akabiru changed the title Use displayId in work package URLs for human-readable routing Use displayId in work package URLs for semantic identifier routing Apr 13, 2026
@akabiru akabiru force-pushed the implementation/73797-use-displayid-in-work-package-urls branch from 6574c91 to 530eecc Compare April 13, 2026 11:36
@akabiru akabiru self-assigned this Apr 13, 2026
@akabiru akabiru force-pushed the feature/73717-adapt-work-package-lists-for-project-based-semantic-work-package-identifiers branch 2 times, most recently from ac24e63 to 42408c8 Compare April 14, 2026 15:30
@akabiru akabiru force-pushed the implementation/73797-use-displayid-in-work-package-urls branch from 6aa7c34 to 1801afd Compare April 14, 2026 15:34
@akabiru akabiru force-pushed the feature/73717-adapt-work-package-lists-for-project-based-semantic-work-package-identifiers branch from 42408c8 to bf9dcb1 Compare April 15, 2026 16:38
@akabiru akabiru force-pushed the implementation/73797-use-displayid-in-work-package-urls branch from 1801afd to e37e60f Compare April 15, 2026 16:39
These templates were still using hard-coded #{{ item.id }} which shows
the numeric PK instead of the semantic identifier in search dropdowns
and autocomplete results.
@akabiru akabiru force-pushed the feature/73717-adapt-work-package-lists-for-project-based-semantic-work-package-identifiers branch from bf9dcb1 to a556307 Compare April 15, 2026 17:03
akabiru added 3 commits April 15, 2026 20:03
Routes already accept semantic identifiers (e.g. PROJ-7) via
WP_ID_URL_PATTERN, but all frontend-generated links still used
numeric PKs. This wires displayId into every navigation path so
the browser URL bar shows the semantic form when available.

Key design decision: data-work-package-id attributes on <a>
elements stay numeric — the selection/hover system is keyed by
PK. Only the href gets the semantic ID via a new optional
routingId parameter on UiStateLinkBuilder.

Changes span table links (ID column, linked WP fields, details
action), navigation handlers (list view, embedded tables, boards,
BCF, calendar), breadcrumbs, tabs, hierarchy, single view,
context menu, quickinfo macro, and post-creation redirect.

API-only paths (time entries, share, hover cards, progress modal)
are deliberately left with numeric IDs — they never appear in the
address bar.
Stateful navigation changes ($state.go callers) moved to a
follow-up PR with P1 bug fixes for focus/selection, card
highlight, bulk delete, and cache coherence.

Retained changes only modify href attributes and URL strings —
click handlers still read data-work-package-id (numeric PK),
so no semantic IDs leak into Angular state params.
@akabiru akabiru force-pushed the implementation/73797-use-displayid-in-work-package-urls branch from e37e60f to 6b119b4 Compare April 15, 2026 17:03
akabiru added 7 commits April 15, 2026 20:04
Routes already accept semantic identifiers (e.g. PROJ-7) via
WP_ID_URL_PATTERN, but all frontend-generated links still used
numeric PKs. This wires displayId into every navigation path so
the browser URL bar shows the semantic form when available.

Key design decision: data-work-package-id attributes on <a>
elements stay numeric — the selection/hover system is keyed by
PK. Only the href gets the semantic ID via a new optional
routingId parameter on UiStateLinkBuilder.

Changes span table links (ID column, linked WP fields, details
action), navigation handlers (list view, embedded tables, boards,
BCF, calendar), breadcrumbs, tabs, hierarchy, single view,
context menu, quickinfo macro, and post-creation redirect.

API-only paths (time entries, share, hover cards, progress modal)
are deliberately left with numeric IDs — they never appear in the
address bar.
Replaces 5 identical private resolveRoutingId methods with
a shared utility function that accepts States as a parameter.
Defer focus and selection to init() (called after WP loads) so we
use this.workPackage.id (always numeric) instead of the route param
which may be a semantic identifier like "PROJ-7".
…displayId

The route param may be a semantic identifier ("PROJ-7") which won't
match workPackage.id (numeric "42"). Comparing against displayId
as well handles both classic and semantic modes.
Resolve $state.params.workPackageId to numeric PK via the States
cache before comparing against the deleted IDs array.
Normalize this.workPackageId from semantic (e.g. "PROJ-7") to
numeric PK after first WP load, ensuring downstream cache lookups
use the canonical key.
Document the dual-purpose contract in observeWorkPackage (cache
normalization), deferred focus in split view init, card highlight
comparison logic, and best-effort fallback in resolveRoutingId.
@akabiru akabiru force-pushed the feature/73717-adapt-work-package-lists-for-project-based-semantic-work-package-identifiers branch from e1a7da3 to e1cac12 Compare April 15, 2026 17:56
akabiru added 3 commits April 16, 2026 10:48
The displayId getter on WorkPackageBaseResource already handles the
fallback to numeric id, so callers don't need to repeat the
`displayId ?? id!` pattern. This simplifies 7 call sites across
breadcrumbs, tabs, relations, single-view, and display fields.

The formatting regex that decides whether to prefix with # (classic
mode) or not (semantic mode) was duplicated in WorkPackageBaseResource
and WorkPackageDisplayField. Extract it into a shared
formatWorkPackageId() function in work-package-id-pattern.ts alongside
the existing WP_ID_URL_PATTERN constant.
Clicking a semantic ID link (e.g. PROJ-42) in the work package table
navigated to the numeric ID URL (/work_packages/42) instead of the
semantic one (/work_packages/PROJ-42). The href was correct but the
click handler (WorkPackageStateLinksHandler) read data-work-package-id
(the numeric PK) and used it for navigation.

Add data-routing-id to link elements carrying the semantic identifier.
The click handler now reads this attribute and flows it through the
stateLinkClicked event so wp-list-view uses it for URL construction
while keeping the numeric PK for selection and focus state.
Absorb PR #22739 (stateful navigation changes) into this branch
so all displayId routing work lives in a single PR.

Conflict resolution in wp-list-view: openStateLink now prefers
event.routingId from the data attribute, falling back to
resolveRoutingId (state store lookup) for callers that don't
carry the routing ID on the event.
@akabiru akabiru changed the title Use displayId in work package URLs for semantic identifier routing Use displayId in work package URLs and stateful navigation Apr 16, 2026
Make resolveRoutingId protected in the parent class so BcfListComponent
inherits it instead of redeclaring its own private copy, which TypeScript
rejects as conflicting declarations.
Comment on lines +10 to +27
/**
* Format a work package identifier for inline UI display.
*
* OpenProject supports two identifier modes:
* - **Semantic**: project-scoped identifiers like `PROJ-42` that contain letters.
* These are self-describing and returned as-is.
* - **Classic**: numeric-only identifiers like `42`.
* These are prefixed with `#` to visually distinguish them as WP references.
*
* @example
* formatWorkPackageId('PROJ-42') // => 'PROJ-42'
* formatWorkPackageId('42') // => '#42'
* formatWorkPackageId('') // => ''
*/
export function formatWorkPackageId(id:string):string {
if (!id) return '';
return /[A-Za-z]/.test(id) ? id : `#${id}`;
}
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.

cc @HDinger 🙂

@akabiru akabiru marked this pull request as ready for review April 16, 2026 16:45
@akabiru akabiru requested review from a team April 16, 2026 16:45
@akabiru
Copy link
Copy Markdown
Member Author

akabiru commented Apr 16, 2026

Hi @HDinger, could you please help me examine this more closely? I’m not very familiar with these parts 🙏🏾

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant