Skip to content

feat(client): enable sorting the chore admin tables - #183

Merged
Szeraax merged 1 commit into
jherforth:mainfrom
mrramam:feat/sort-chore-admin-lists
Sep 25, 2026
Merged

Szeraax merged 1 commit into
jherforth:mainfrom
mrramam:feat/sort-chore-admin-lists

Conversation

@mrramam

@mrramam mrramam commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

The Chore Definitions and Chore Schedules tables in Settings render rows in whatever order the API returns them. Neither /api/chores nor /api/chore-schedules declares an ORDER BY, and ChoreSchedulesTab filters without sorting, so the order is whatever SQLite's plan yields. In practice that is insertion order, which is hard to scan past a handful of chores, and it is not a guarantee: an added index or an ANALYZE could reorder both tables with no code change.

Sorts by title in the client rather than adding ORDER BY to the endpoints, since the dashboard widget consumes the same two and its ordering is a separate question. The two chore pickers on the tab, the filter and the schedule form's chore select, follow the same order. Widget ordering is unchanged.

compareByTitle folds case and accents so one chore does not sort into a second alphabet, and breaks ties on id so equal titles keep a deterministic order rather than depending on an input order that is itself unspecified.

Six unit tests on the comparator.

@Szeraax

Szeraax commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator

This PR looks solid and I'd have no problem with merging it in.

However, could you take a quick pass and see if its yucky to instead make all chores and chore schedules' table headers clickable to sort by that column? e.g. can click on Clams once to sort by clam value asc. And a 2nd time to sort by clam value dsc. Or click on Assigned To, Next Occurrence, etc. If we can do that without being a massive amount of bloat, I'd love to go that route, possibly with the default sorting to be client side by title/ID as you proposed here.

@jherforth

Copy link
Copy Markdown
Owner

Clickable headers sounds even better. We can default to sort by title ASC.

`GET /api/chores` and `GET /api/chore-schedules` declare no `ORDER BY`, so
rows arrived in insertion order, which stops being scannable at around thirty
chores. Both tables now open sorted by title ascending and re-sort on a header
click.

A column is sortable only where its order answers a question: Title, Clams and
Schedules on definitions, Chore, Assigned To, Next Occurrence and Clams on the
schedule. Description is free text and usually blank, Duration is four modes
rather than a length, and Visible is a live Switch, so sorting by it would
slide the row out from under the click that toggled it.

Next Occurrence sorts on the parsed date, not the rendered label, which would
order "Apr" before "Jan"; `getNextOccurrence` is split so the label and the key
share one parse. Schedules with no occurrence, one-time or unparseable, sort
last in both directions rather than crowding out the soonest one. Keys are
computed once per row rather than inside the comparator, which would reparse a
crontab on every comparison. The chore pickers keep their own alphabetical
list, so a dropdown does not reorder because the table above was sorted by clam
value. Below the mobile cutoff `stackableTableSx` hides the header row, so
phones get the default order and no control.
@mrramam
mrramam force-pushed the feat/sort-chore-admin-lists branch from 0aa80bf to 149bd21 Compare September 25, 2026 21:11
@mrramam

mrramam commented Sep 25, 2026

Copy link
Copy Markdown
Contributor Author

Both tables now have clickable headers via MUI TableSortLabel and open at title ASC.

I did not make every header sortable, though. Seven of twelve, on the rule that a header which invites a click should answer a question someone actually asks:

table sortable
Chore Definitions Title, Clams, Schedules
Chore Schedule Chore, Assigned To, Next Occurrence, Clams

Left alone, and happy to add any of them if you disagree:

  • Description is free text and usually empty. Sorting puts all the blanks at one end and otherwise orders by an arbitrary first word.
  • Duration is not a length, it is four modes (until-completed, once-completed, day-of, none). No natural order, so sorting it is really grouping.
  • Visible is a live Switch. Sorting by it means toggling a row slides that row out from under the click that toggled it.

Schedules ascending turns out to be the surprise favorite: it surfaces chores defined but scheduled for nobody.

Three things worth calling out in the diff.

Next Occurrence sorts on the parsed date, not the rendered label. The cell renders a localized toLocaleDateString, so sorting the text would put "Apr" before "Jan". getNextOccurrence is split into nextOccurrenceAt (returns the Date, or null) plus the formatter, so the label and the sort key share one cron parse.

Rows with no occurrence sort last in both directions. A one-time task, or a crontab that fails to parse, has no next occurrence at all. Reversing the arrow to find the soonest one should not march those to the top, so a null key is always last and only the real dates reverse.

The chore pickers needed their own list. The filter Select and the picker inside the schedule dialog were both mapping the same sorted array as the definitions table. Once that table can be sorted by clam value, the dropdowns would silently reorder with it, so they keep an alphabetical list of their own.

compareByTitle from the previous round is folded into one compareByKey(a, b, keyOf, direction). It had become a single-caller special case, and keeping both would have meant two comparators with two different rules for missing values. Its tests carried over and five more were added, including the null-last-both-directions case and a numeric one that catches '10' sorting before '9'.

One known limit: below the mobile cutoff, stackableTableSx hides thead and the rows become stacked cards, so small screens get the default order and no sort control. That is why a good default matters, and title ascending is it.

No new translation keys. aria-sort comes from sortDirection on the TableCell, so there is no visually hidden string to translate. npm test in client/ is 22 files and 321 tests green, check:i18n passes, and the build is clean. Running on our own install since 2026-09-25.

@mrramam mrramam changed the title feat(client): sort the chore admin tables alphabetically feat(client): enable sorting the chore admin tables Sep 25, 2026
@Szeraax
Szeraax merged commit f27351e into jherforth:main Sep 25, 2026
2 checks passed
@Szeraax Szeraax added this to the 1.9 milestone Sep 25, 2026
@Szeraax Szeraax added the enhancement New feature or request label Sep 25, 2026
@Szeraax
Szeraax self-requested a review September 25, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants