Skip to content

Development: Let applicants choose jobs per page - #2501

Open
az108 wants to merge 15 commits into
mainfrom
feat/jobs-per-page-840
Open

Development: Let applicants choose jobs per page#2501
az108 wants to merge 15 commits into
mainfrom
feat/jobs-per-page-840

Conversation

@az108

@az108 az108 commented May 11, 2026

Copy link
Copy Markdown
Collaborator

Checklist

General

Client

  • Important: I implemented the changes with a very good performance, prevented too many (unnecessary) REST calls and made sure the UI is responsive, even with large data (e.g. using paging).
  • I strictly followed the principle of data economy for all client-server REST calls.
  • I strictly followed the client coding and design guidelines.
  • I added multiple integration tests (Vitest) related to the features (with a high test coverage), while following the client test guidelines.
  • I documented the TypeScript code using JSDoc style.

Motivation and Context

Closes #840. Pagination across the app was fixed at the server-default page size, so anyone scanning a long list paid for repeated round-trips with no way to change the page. The size dropdown is added to the shared dynamic-table, so every paginated view inherits it, and the job overview moves onto that shared component.

Description

Choosing a page size

  • dynamic-table gains a rowsPerPageOptions input, defaulting to [5, 10, 15, 20], which surfaces the PrimeNG size dropdown next to the paginator. Nothing bound that input before, so no view had a size dropdown at all until now.
  • The sizes stop at 20 because no view in the app comes close to needing more, and thirteen of the fifteen page sizes in the app start at 10, so the smaller steps give a useful choice in both directions.
  • The job overview has its own [6, 12, 18, 24] and still opens on 12. Its cards are not a grid but a flex-wrap row of min-w-[16.5rem] items in a max-w-[104rem] container, so up to six fit on a row and fewer as the window narrows. Twelve fills two full rows at the widest; ten stranded four cards on a second row. No single number divides evenly at every width — 12 and 24 divide by 1, 2, 3, 4 and 6, while 18 leaves a short last row at four per row — but the default is exact and the rest are close.
  • The admin dependency list likewise keeps its own [25, 50, 100]: it runs to hundreds of entries and starts at 25, which the shared sizes do not offer, so its dropdown had nothing selected.
  • The shared sizes stay at [5, 10, 15, 20] for the tables, which have no row to fill.

Remembering it

  • Opt-in persistence through the new storageKey input. When set, the size the reader picks is written on every change and applied on the next visit. Only the job overview opts in so far.
  • LocalStorageService exposes loadPageSize(key, fallback, allowed?) and savePageSize(key, size). The loader checks the stored value is numeric, positive and one of the sizes on offer, so a stale or tampered key falls back rather than showing a size that is not in the dropdown.

Smaller screens

  • A full page is a long scroll on a phone, so a view starts on the shortest size it offers there: 5 for the tables, 6 for the job cards. Taking it from the offered sizes rather than fixing it at 5 keeps it valid for views that set their own. A remembered size normally wins over this.
  • The job overview is the exception, via alwaysUseMobileRows: its cards are tall, so a full page is a long scroll whatever size was last picked on a wider screen, and it opens short on a phone every time. This means a size picked on a phone lasts for that visit only.
  • The check runs once at first render rather than tracking resizes, so rotating a tablet mid-visit does not repaginate.

Who fetches the first page

  • The table now tells PrimeNG not to fire the first load and issues it itself, once the page size is known.
  • This is what makes the two features above work at all. PrimeNG assigns its rows input straight to a field and only reloads on init, paging, sorting and filtering, so a size applied from our side never reached the server: the overview reported five per page on a phone and listed ten, and a reader with a remembered size of twenty was shown ten under a label saying twenty. Reloading after the fact would have raced the load already in flight, which no caller guards against.
  • The request the table makes does not touch storage, since a size nobody chose should not become the remembered one.

Also

  • The interviewee picker sized its page to the number of people it was showing and never listened for page changes, so its paginator only ever had one page and its dropdown did nothing. It no longer has a paginator.

Steps for Testing

Prerequisites:

  1. Log in as an applicant.
  2. Open the job overview page.

Steps:

  1. Confirm the paginator shows a size dropdown offering 6, 12, 18 and 24, with 12 selected, and that 12 cards fill their rows with none stranded.
  2. Switch to 24 and confirm the list actually grows to twenty-four cards, not just the label. Page forward and back — the size sticks.
  3. Reload, or open the page in a new tab. The size you picked is applied, and again the list matches the label.
  4. Clear jobsPerPage from localStorage and reload. The size falls back to the default.
  5. Set jobsPerPage to a size that is not on offer, say 10, and reload. It is ignored rather than shown.
  6. Narrow the window below 768px, clear jobsPerPage, and reload. The overview opens on 6 cards, and six is selected in the dropdown.
  7. Still narrow, switch to 24, then reload. The overview is back on 6, since the job overview always opens short on a phone.
  8. Widen the window again and reload. The size you picked on the phone applies.
  9. Open any other paginated view (applications, positions, research group members) narrow and confirm it opens on 5 — the shortest size those tables offer — and wide that it opens on its usual size.
  10. Open the admin dependency list and confirm it offers 25, 50 and 100, with 25 selected.
  11. Open the interviewee picker when assigning an applicant to a slot and confirm it has no paginator.

Review Progress

Code Review

  • Code Review 1

Manual Tests

  • The dropdown changes the number of rows actually loaded, not just the label
  • The chosen size survives paging and reloads
  • A stored size that is not on offer is ignored
  • Phone-sized viewports open on the shortest size the view offers, and the job overview does so on every visit
  • A full page of job cards leaves no stranded row at a range of window widths
  • The admin dependency list keeps its own larger sizes
  • The interviewee picker has no paginator

Screenshots

To be added.

@github-actions github-actions Bot added client Pull requests that update TypeScript code. (Added Automatically!) tests labels May 11, 2026
@codacy-production

codacy-production Bot commented May 11, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 53 complexity

Metric Results
Complexity 53

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Surfaces a 10/20/30/40/50 page-size dropdown on every paginated view
that uses the shared dynamic-table component. When a storageKey input
is supplied the chosen size is hydrated from localStorage on init and
persisted on every change, so it survives navigation and reloads.

Migrate the job overview's standalone p-table to dynamic-table and wire
it up with storageKey="jobsPerPage". The lazy-load event continues to
carry the new rows value to the server.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@az108
az108 force-pushed the feat/jobs-per-page-840 branch from 790f362 to 2bcc3ed Compare May 12, 2026 09:08
@github-actions

Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions github-actions Bot added the stale label May 19, 2026
@az108 az108 removed the stale label May 19, 2026
# Conflicts:
#	src/main/webapp/app/job/job-overview/job-card-list/job-card-list.component.ts
az108 and others added 2 commits May 20, 2026 00:51
@github-actions

Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions

Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions

Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions github-actions Bot added the stale label Jul 2, 2026
@github-actions

Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

Cathy0123456789 and others added 5 commits July 18, 2026 01:22
The page sizes on offer went up to 50, which no view in the app comes
close to needing. Almost every table starts at 10, so the smaller steps
give a useful choice in both directions instead of four options nobody
would pick.

It also fixes the member picker, which starts at 5 and so had no matching
entry in its own dropdown.

The specs used 30 as their stand-in for a second valid page size. The
hydration test would now fail on that, since the stored value is checked
against the options on offer and would fall back rather than be applied.

Co-Authored-By: Claude <noreply@anthropic.com>
A full page of rows is a long scroll on a phone, so tables now start on
the smallest page size there. A size the reader picked before still wins,
and the choice is not written to storage, so widening the window later
does not leave them stuck on a short page. Tables that do not offer five
keep the size they asked for.

The dependency list runs to hundreds of entries and starts at 25, which
the shared sizes do not offer, so its dropdown had nothing selected. It
now offers sizes of its own.

The interviewee picker sized its page to the number of people it was
showing and never listened for page changes, so its paginator only ever
had one page and its size dropdown did nothing. It no longer has one.

Co-Authored-By: Claude <noreply@anthropic.com>
Only the job overview listened for the resolved page size, so everywhere
else still opened a full page on a phone. Each table now binds it, which
also gets them the stored preference for free if a view later opts into
one.

The binding writes straight to the page-size signal rather than adding a
setter to eleven components. Every one of these tables already reloads
from the table's lazy-load event, so changing the size reloads as before.

The dependency list and the interviewee picker stay out: the first offers
its own larger sizes and never five, the second has no paginator.

Co-Authored-By: Claude <noreply@anthropic.com>
@az108

az108 commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator Author

Small correction to my own commit message on 26466f8: it claims the new page sizes also fixed the add-members picker, which "starts at 5 and so had no matching entry in its own dropdown". That is wrong — that picker uses a bare <p-paginator> with no rowsPerPageOptions, so it has no size dropdown at all and is unaffected either way. The change itself stands; only that sentence is inaccurate.

az108 and others added 2 commits July 26, 2026 22:54
Job cards are tall, so ten of them is a long scroll on a phone whatever
size the reader last picked on a wider screen. The overview now keeps the
short page on small screens every visit.

This stays opt-in. Everywhere else a remembered size still wins, and the
short page only applies when nothing has been remembered yet.

Co-Authored-By: Claude <noreply@anthropic.com>
Choosing the page size after the first load only relabelled the paginator.
PrimeNG assigns the rows input straight to a field and reloads on init,
paging, sorting and filtering, so a size set from our side never reached
the server: the overview reported five per page on a phone and listed ten,
and a reader with a remembered size of twenty was shown ten under a label
saying twenty.

Reloading afterwards would have raced the load already in flight, which no
caller guards against, so the table takes over the first load instead. It
tells PrimeNG not to fire it, works out the page size, and requests the
first page itself. That keeps it to a single request, made with the size
the reader ends up seeing.

The request goes out without touching storage, since a size nobody chose
should not become the remembered one.

Co-Authored-By: Claude <noreply@anthropic.com>

@Cathy0123456789 Cathy0123456789 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.

Tested locally, maybe we can adapt the number of cards per row?

Comment thread src/main/webapp/app/job/job-overview/job-card-list/job-card-list.component.ts Outdated
az108 and others added 2 commits July 30, 2026 13:02
# Conflicts:
#	src/main/webapp/app/job/job-overview/job-card-list/job-card-list.component.ts
The cards wrap into rows of up to six, so twelve filled two rows exactly.
Ten left four cards stranded on a second row, and the shared sizes offered
nothing that divided evenly.

The overview keeps its own sizes of six, twelve, eighteen and twenty-four
and opens on twelve again. The shared sizes stay as they are, since a table
has no row to fill, and the size dropdown already takes per-view sizes, as
the dependency list does.

Small screens now start on the shortest size a view offers rather than a
fixed five, so the overview starts on six there instead of falling back to
a size it does not offer. A phone shows one card per row either way.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

@github-actions

Copy link
Copy Markdown
Contributor

There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions.

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

Labels

client Pull requests that update TypeScript code. (Added Automatically!) stale tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pagination - Items per Page

2 participants