Conversation
Both list endpoints take `sort_by` and `sort_order`, validated against the keys the service publishes so an unknown column is a 422 rather than a silently ignored parameter. Projects order by name, status, due date, created date or rate; tasks by number, name, priority, due date or created date. The sort runs in PHP over the collection the service already holds, not in the query. Priority ranks LOW, NORMAL, HIGH, URGENT, which is not its alphabetical order, and the three supported databases disagree about where a NULL lands, so ordering here gives one answer everywhere without a CASE expression or a `NULLS LAST` clause SQLite would refuse. It costs nothing extra: every list endpoint already loads the whole collection and cuts the page from it. A row with no value sorts last in both directions, and ties break on the id in the direction of the sort, so a page boundary never drops or repeats a row. Naming a column reads it ascending unless the caller says otherwise. The projects list now opens newest first rather than alphabetically, which is how the host's own lists open; two existing order assertions move with it.
A range, four figures per currency, a billable split and three breakdowns of the same range: by project, by member and by customer. Everything comes from one call to `reports/summary`, which already aggregates per currency and never converts between them, so the page never adds two currencies together either. The range offers this week, this month, last month, this quarter and this year, and switches to Custom the moment a date is picked by hand. "This week" follows the company's week-start setting, so the report and the timesheet agree about where a week begins. The page asks only for `view-own-time`. The endpoint narrows the report to the caller's own time rather than refusing them, so gating on `view-all-time` would hide a screen that works. The split bar charts minutes rather than money, because minutes are the one figure that adds up across currencies, and it is drawn with two tokened divs rather than a chart library the bundle would have to carry. The payload is read defensively throughout: a missing figure becomes zero and a missing list an empty one, which renders as an empty table rather than as a blank screen.
The project columns name, status, rate and due date, and the task columns number, name, priority and due date, are now sortable headers. `BaseTable` reports the column key it is sorting on and the empty order it starts in, so `sortParams` maps that to the `sort_by` and `sort_order` the endpoints take and adds nothing at all until a header is clicked. Contacts stop reading as `#42`. A small store holds one map of contact id to display name per company session, filled the first time a screen renders a row that has one and dropped on `company:changing`. The projects table, the board card tooltip and the project detail header all read it, which also retires the detail page's single-contact fetch. The map is capped at 200 contacts on purpose: it exists to label ids the module already holds, not to browse the address book, and forms keep their search-based picker for choosing one. Anything past the cap falls back to `#id`, as does a contact the caller may not read. The two project pickers ask for their list by name, because a picker reads alphabetically whatever order the endpoint itself opens in.
Assets ship compiled, so the package installs without a build step on the target system.
Contributor
Author
|
Superseded by #14, which carries this stack consolidated into three commits on top of main. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Milestone 7: the reporting screen, plus the two list improvements the screens
around it were missing. Stacked on
feat/ui-time.Backend: the lists choose their order
GET projectstakessort_byinname,status,due_date,created_at,default_rate;GET taskstakessort_byinnumber,name,priority,due_date,created_at. Both takesort_orderinasc,desc, bothvalidated in the list request against the keys the service publishes, so an
unknown column is a 422 rather than a silently ignored parameter.
The sort runs in PHP over the collection the service already holds, not in the
query. Priority ranks LOW, NORMAL, HIGH, URGENT, which is not its alphabetical
order, and the three supported databases disagree about where a NULL lands, so
ordering here gives one answer everywhere without a CASE expression or a
NULLS LASTclause SQLite would refuse. It costs nothing extra: every listendpoint already loads the whole collection and cuts the page from it in
Controller::paginate.A row with no value sorts last in both directions, and ties break on the id in
the direction of the sort, so a page boundary never drops or repeats a row.
One behaviour change to review. The projects list now opens
created_at descrather than alphabetically, which is how the host's own lists open. Twoexisting order assertions moved with it, and the two in-module project pickers
now ask for their list by name so they stay alphabetical.
Frontend
number, name, priority and due date.
BaseTablereports the column key itis sorting on and the empty order it starts in, so
sortParamsmaps that tothe endpoint's parameters and adds nothing until a header is clicked.
display name per company session, filled the first time a screen renders a
row that has one and dropped on
company:changing. The projects table, theboard card tooltip and the project detail header read it, which retires the
detail page's single-contact fetch. Capped at 200: it labels ids the module
already holds rather than browsing the address book, and forms keep their
search-based picker.
/admin/modules/tasks-projects/reports, reachable fromthe projects header next to Board. A range with presets, four figures per
currency, a billable split drawn with two tokened divs rather than a chart
library, and three breakdowns of the same range: by project, by member and
by customer. It asks only for
view-own-time, because the endpoint narrowsthe report to the caller's own time rather than refusing them.
The payload is read defensively throughout: a missing figure becomes zero and
a missing list an empty one, which renders as an empty table rather than as a
blank screen.
Checks
composer run lint,composer run test(236 green, up from 229),pnpm run lint,pnpm exec tsc --noEmit,pnpm run buildwithgit diff --exit-code -- distclean, and both module validators.The bundle still has exactly one
window.__invoiceshelf_vuedestructuring andno
from "vue". Not yet exercised in a browser.https://claude.ai/code/session_01DCf36XDKprZifej8dc2r1E