Skip to content

quick search: sort toggles for the dropdown results (stacked on #1563) - #1585

Merged
jbtronics merged 12 commits into
Part-DB:masterfrom
samyk:quicksearch-sort
Oct 10, 2026
Merged

jbtronics merged 12 commits into
Part-DB:masterfrom
samyk:quicksearch-sort

Conversation

@samyk

@samyk samyk commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

This is stacked on #1563 (live dropdown search honors regex, extensive and field options) and contains its commit; please review that one first. Once it is merged this PR will only show its own changes. Both touch part_search_controller.js, TypeaheadController and PartRepository, which is why it is built on top instead of beside it.

The quick search dropdown always lists its results by name. With a few hundred matches the one you want is often far down, and the only way to get another order is to submit the search and sort the results table.

This adds a small row of toggles to the header of the dropdown to order the results by: name, manufacturer, supplier, created date, last modified date, top-level category, or category.

  • First click sorts ascending, a second click descending, a third returns to the default order. Only one is active at a time; the active one is filled in the primary color with a direction arrow.
  • Clicking a toggle keeps the focus in the search field and the dropdown open; the results refresh in place. Keyboard navigation of the results is unchanged.
  • The choice is remembered in localStorage and shared by the navbar and homepage search fields.
  • When ordered by something a result does not show anyway (manufacturer, supplier, a date, the top-level category), each result shows that value on its own line, marked with the ordering's icon.
  • Submitting the search opens the results table in the same order for name, manufacturer, created date, last modified and category. A URL-requested order takes precedence over the saved table state and does not overwrite it.

Server side:

  • The typeahead endpoint takes sort and sort_dir; an unknown sort falls back to the default. NULLs sort last in both directions, ties by name and then id.
  • Supplier is the alphabetically first supplier name across the part's order details.
  • The top-level category is not stored, so it is resolved in PHP from the matching parts' category ids plus one query for the category tree.
  • PartSearchSort holds the sort keys, the request parsing and the mapping to a results table column.

The toggle arrow always takes its space and is only made visible on the active toggle, so the toggles do not move when one is chosen.

Ten new strings in frontend.en.xlf (the JS translator only reads that domain).

Notes for review:

  • Supplier and top-level category do not carry over to the results table, as the table has no such columns; it opens name-sorted for those. parts table: supplier and supplier part number columns #1574 adds a sortable supplier column, after which mapping it is one line in PartSearchSort::getTableColumn().
  • The dropdown only fetches the first 100 matches, so the sort covers those.
  • Tested on sqlite with ~18k parts (every key in both directions in about 0.03-0.1 s); MySQL and PostgreSQL run in CI but were not exercised by hand.
  • Top-level category is compared case-insensitively in PHP, while the other text orderings follow the database's NATSORT setting.
  • The toggles only show once the dropdown has results.

The navbar/homepage dropdown only did a fixed substring match on
name/description/category/footprint; regex and extensive matching needed
Enter and a full results page.

When regex or extensive matching is checked in the search options, the
dropdown now sends the checked options to the typeahead endpoint, which
runs the same PartSearchFilter as the search page (selected fields,
regex, extensive, wildcard). With neither checked the request and the
existing fast autocomplete search are unchanged.

A half-typed regex (e.g. "lm(") answers an empty list instead of an
error, highlighting follows the regex or the individual words, and
toggling an option re-runs the dropdown for the text already typed.
The request-to-filter code moves from PartListsController to
PartSearchFilter::fromRequest() so both places share it.
The search dropdown always listed its results by name, so finding e.g. the
most recently added part matching a keyword meant submitting the search and
sorting the table. The header of the dropdown now has a row of small toggles
to order the results by name, manufacturer, supplier, creation date, last
modification, top-level category or category. A click sorts ascending, a
second one descending and a third one returns to the default ordering. The
choice is remembered in the local storage and shared by all search fields.

The typeahead endpoint takes the ordering as "sort" and "sort_dir" query
parameters. Parts without a value (e.g. no manufacturer) are put last in
both directions and ties are ordered by name. The root category is not
stored in the database, so that ordering is resolved in PHP from the IDs
and category IDs of the matching parts.

The search form submits the same parameters, and the results table opens
sorted by the matching column where it has one (name, manufacturer,
creation date, last modification, category). Such a requested ordering takes
precedence over the saved table state, but does not overwrite it.
Ordered by supplier, manufacturer, a date or the top level category, the
dropdown gave no hint why a result was where it was: each result now shows
that value (marked with the ordering's icon). The toggles were small enough
to miss and to mis-click; they are the size of the result text now.
The direction arrow was only rendered in the active toggle, which made it
wider and pushed its neighbours aside: the arrow now always takes its space
and is just invisible. The active toggle used the body background as text
color, which is dark on the primary color in dark mode; it is white now.
phpstan (arrayFilter.strict) rejects array_filter without a callback.
@codecov

codecov Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 22.09945% with 141 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.18%. Comparing base (a27ebb9) to head (172b60b).
⚠️ Report is 29 commits behind head on master.

Files with missing lines Patch % Lines
src/Repository/PartRepository.php 1.88% 104 Missing ⚠️
src/Controller/TypeaheadController.php 15.62% 27 Missing ⚠️
src/DataTables/Filters/PartSearchSort.php 30.76% 9 Missing ⚠️
src/Controller/PartListsController.php 83.33% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1585      +/-   ##
============================================
+ Coverage     62.97%   64.18%   +1.21%     
- Complexity    10071    10270     +199     
============================================
  Files           748      763      +15     
  Lines         32315    32868     +553     
============================================
+ Hits          20351    21097     +746     
+ Misses        11964    11771     -193     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@samyk

samyk commented Oct 4, 2026

Copy link
Copy Markdown
Contributor Author

Example of default search vs sorted search (my mouse is over the Supplier icon in the 2nd image, but doesn't show up in screenshot, hence the alt text and colored icon). The sorted value also shows up in the parts when enabled (sometimes I want to find things I most recently purchased for example)

CleanShot 2026-10-04 at 16 29 21@2x CleanShot 2026-10-04 at 16 28 57@2x

@jbtronics

Copy link
Copy Markdown
Member

Thats a cool feature.

I fixed the styling of the buttons, improved code structure, and optimized the code to avoid sorting in PHP (we can directly request entities sorted by a list of ids from the database)

@jbtronics
jbtronics merged commit 300d0f8 into Part-DB:master Oct 10, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants