Engineering
Why We Built Time Tracking Reports as What, Who, and When
Time tracking reports fail in two opposite ways. Either they dump a spreadsheet of every entry and call it “flexible,” or they hide behind a query builder that looks like a BI tool a department would staff. Consultants need neither. They need to answer one sentence: who did what, for which client, in this date range — and then turn that answer into a chart, a pivot, a CSV, or an invoice without rebuilding the question.
MinuteShark reports are a triad, not a language. You pick What (clients or projects, optionally tasks), Who (users or teams), and When (a date range). Generate. Then you get a grand total, a Chart.js chart, a table you can pivot, and exports. If rounding is on, you also get a BILLABLE total. The engineering choice was to make “generate” cheap and honest, not to invent SQL for a solo freelancer’s Friday.
Why a query builder is the wrong product for this job
We ship an Ionic + Angular app. The people who open Reports are not writing GROUP BY after a client workshop. They are trying to remember whether last week’s “Website Redesign” still looks like the quote. If the first screen is twelve filter chips and a saved-view picker, they will export History and rebuild the week in a spreadsheet — which means we failed even if the database could have answered the question.
The triad is opinionated on purpose. Time lives on a task. Tasks live under projects, projects under clients, inside a workspace that does not share a catalog with another workspace. “What” therefore starts at clients or projects, with tasks as an optional sharpening — not at a free-text field that can mean anything. “Who” is users or teams, because a tiny consulting group needs to split a week without pretending they are a 50-person PMO. “When” is a date range with previous/next period, because the useful comparison is almost always “this week vs last week,” not an arbitrary fiscal custom dimension.
That is also why dashboard search (Algolia) and Reports are different surfaces. Search is for starting a timer or creating a task in the moment. Reports are for after you already did the work. Mixing them produces a screen that is bad at both.
Generate first. Pivot second.
The sequence is load-bearing. You generate once from What × Who × When. Then you can pivot the same result by date, client, project, task, user, or team. You can inline-edit an entry if you spotted a mis-click. You can step to the previous or next period without re-selecting the catalog.
If we had let every pivot be a new server query with a slightly different filter set, people would not trust that the grand total was the same object they just looked at. One generated result, many views of it, is a simpler mental model — and a simpler cache. Firestore holds the entries. Functions shape the window. The Angular view does not get to invent a second “maybe this is the week” while you are staring at the first.
The triad
What × Who × When
Clients or projects (optional tasks) · users or teams · a date range
Then: totals, chart, table, pivot, export, invoice
Why Chart.js, and what the chart is allowed to mean
We render the picture with Chart.js. Not because consultants asked for a visualization library by name, but because a week you cannot see is a week you will argue with. The chart is a readout of the generated result — the same numbers as the table — not a second metric store with its own rounding rules.
Two totals matter when rounding is on. Original duration is always stored. The rounded value shows as BILLABLE. The chart and the grand total have to respect that split or invoices will not match the report you just nodded at. There is a midnight edge: rounded totals include the full rounded value for entries that started in range even if they cross midnight. That rule is easier to explain than to discover in a pile of custom filters, so it lives in the generate path, not in a user-authored formula.
We did not bolt on a “dashboard of dashboards.” If you want widgets, those live on the daily dashboard — starred tasks, a private notes widget, to-dos, Pomodoro, today’s history. Reports are the place you go when the question is money and the week, not the place we stuffed every chart we could configure.
The export is part of the report, not a different product
CSV, Excel (XLSX), and JSON come off the same generated result. We use the obvious libraries for the job — including xlsx for workbooks — because a freelancer forwarding a week to an accountant should not have to screenshot a table. Generate Invoice is also the same object: pick a cost profile (or type rates), get line items from time, add tax lines, export PDF, DOCX, or CSV.
That is why we refused a pretty in-app “invoice widget” that is not a document. If the report cannot become a file a client can open, it is a demo. The reports guide is the customer-facing map of this path; this post is the reason the map is three questions long.
What we left out on purpose
No screenshot monitoring to “verify” the hours. No silent AI rewrite of the table — MinuteShark AI can open Reports with filters and answer how much you tracked from supplied stats, but it does not invent entries while you are reading a chart. No marketplace sync that pretends Jira keys are already invoice-ready. Fewer integrations than enterprise suites is an honest trade. Custom development exists for specialized consulting workflows; a self-serve public API is not a Standard checkbox.
We also did not design Reports as the system of record for a whole company’s OKRs. Primary ICP is freelancers, solo consultants, and tiny teams. If your first requirement is a semantic layer and row-level SSO theatre, you are not underserved by accident. You are outside the design center. Workspaces stay cloud-synced (Firebase Auth, Firestore, Functions) so the same clients and tasks exist on web, iOS, and Android — SOC2 Type II, ISO27001, backups, encryption — but the report UI still has to fit a Friday brain.
How this shows up in a real week
You already have the hierarchy from onboarding: client → project → task. You tracked on tasks, including subtasks if you are on Standard. Friday, you do not reconstruct the week from git or calendar. You generate What = the clients you billed, Who = you (or your two-person team), When = this week. You glance at the chart. You pivot to task if a project looks swollen. You inline-edit the entry you logged on the wrong task. You export, or you generate an invoice with a cost profile.
If you want the consultant-facing version of “which reports to run,” we already published 7 time tracking reports every freelancer should run. If you care why the output is a document, see why a freelancer invoice should be a document. This article is the other half: why the question you type into Reports is three nouns, and why Chart.js is drawing the answer you already generated — not a parallel universe of metrics.
The test we use internally is rude and useful. If a tired person cannot produce last week for one client in under a minute, the triad is too clever. If they can, Chart.js, the pivot, and the export are allowed to exist. Time tracking reports should help you think. They should not become a second job.