Tables: sort by column, choose and order columns, filter by typing in the header #20
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Postulo/postulo#20
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Observation
What exists today
applications/application_list.html): five fixed columns — Role, Company, Location, Status, Applied — in a fixed order, with no sort control. Rows come newest first (Application.Meta.ordering = ("-created_at",)). Filtering is a form above the table: one search box across role, company and location, plus status, tag and open/closed (ApplicationFilterMixin,applications/views.py, line 38). Pages of 50.jobs/company_list.html): five fixed columns — Name, Location, Industry, Postings, Applications — always by name, one search box (CompanyListView,jobs/views.py, line 29). Pages of 50.base.html, line 9) and no template uses it yet. Typing in a header is the first thing in the interface that genuinely wants it.Shape
Three capabilities, one implementation shared by both tables, and later by the reminders and captures lists.
1. Sort by column. Every header is a link that toggles ascending and descending, with an arrow (#8) on the active one:
?sort=applied,?sort=-applied. Sort keys are an allow-list on the view, never a field name taken from the query string. Sortable on applications: role, company, location, status, applied, deadline, priority, last activity. On companies: name, location, industry, postings, applications, last activity. Server-side, because pagination is server-side: sorting in the browser would sort the page, not the list.2. Filter by typing in the header. A small input beneath each header that narrows that column:
?role=python&company=aperture&status=interviewing. Text columns match case-insensitively; status is a select; dates are a from/to pair. Header filters combine with the search box and the existing status and tag filters, which become the same mechanism. Implementation:{% partialdef %}), and the view returns just the partial to an htmx request (django-htmxis already installed);hx-getwithhx-trigger="input changed delay:300ms",hx-targeton the table andhx-push-url="true", so the address bar stays bookmarkable and the back button works;3. Choose and order columns. A Columns control listing every available column with a checkbox and up/down buttons, including columns not shown today — on applications: deadline, priority, channel, salary, tags, last activity, reminders due, next interview (#14); on companies: website, careers page, contacts, a notes excerpt. The choice is per person and per table, stored on the
Profileas a JSON field keyed by table name, so it follows the account to every device. The default reproduces today's tables. Page size (25 / 50 / 100) lives in the same setting.Where state lives. Sort and filters live in the URL: shareable, bookmarkable, back-button-safe. Column layout and page size live on the profile: they are a preference, not a query.
Details
for_user()), so no new exposure.aria-labels ("Filter by company"); everything works from the keyboard.application_board.html) shares the filter mixin: the search, status and tag filters keep working there; header filters are table-only.Classification
Enhancement. Not breaking: new query parameters, one nullable JSON field on the profile, and defaults that reproduce today's tables exactly.
Open questions
app.js); up/down buttons need none. Proposal: buttons first, drag if the buttons grate.