Timesheets
The timesheet records the hours worked in a week or a fortnight, one line per project and task - client work, internal time and leave told apart, billable or not. Type 7:30, 7.5, 45m or a span such as 9-17:30 into any day, or start a timer on a line and stop it when the work is done; times are rounded to the step you set, totalled by day and line, and measured against the hours each day expects.
The rules are checked as you go - too many hours in a day, a task or a note that a project asks for, a closed project, a locked or future day - and the sheet moves from draft to submitted, approved or rejected with a comment and a history of who did what. It reads and writes plain JSON, exports CSV for payroll and billing, and on a phone shows one day at a time.
A consultant's week
A week at a made-up consultancy, part filled in: client work, support, a bid and a day of leave. Fill in the rest, start a timer, add a line, and submit - Wednesday's support line still needs a note, so the sheet will not go until it has one.
// The sheet is plain JSON: save it as it changes, and let the server have the last word on its status.
sheet.addEventListener('bmxChange', async () => {
await fetch('/api/timesheets/2026-W42', { method: 'PUT', body: JSON.stringify(await sheet.getSheet()) });
});
sheet.addEventListener('bmxStatusChange', event => {
if (!userMaySubmit) event.preventDefault(); // the sheet stays where it was
});
A fortnight, for approval
A site engineer's two weeks, submitted and waiting for his manager. The approver's view is read only: check the totals against the hours expected, look at the overtime, then approve it or send it back with a reason. The history underneath keeps every step.
Get the code
<script src="/assets/bmx-components.min.js"></script> <bmx-timesheet id="ts-fn" label="Timesheet, two weeks from 28 September" mode="approve" actor="Helen Brook" today="2026-10-12" locale="en-GB" format="decimal" style="inline-size: 100%"></bmx-timesheet>