← All releases

2026.31.2

Numbers you can trust, notifications that actually arrive, and a phone layout that fits

29/07/2026

Insights was comparing a whole week of rota against the hours clocked so far, so a Tuesday always looked like an underspend - it now compares like with like, prices past shifts at what they were actually paid, and says out loud when open shifts are not counted. Shift reminders no longer depend on a manager having the app open, a failed notification is retried instead of vanishing, and you can turn off the duplicate email. Timesheets and Insights become card lists on a phone, with a tab bar at the bottom and filters behind one button.

Highlights

  • A part-finished week no longer looks like a saving - Insights was comparing every shift in the period against only the hours already clocked, so mid-week always read as a large underspend, coloured green. It now compares against the shifts that have actually ended and says the period is still running.
  • Past weeks keep the pay they were run at - scheduled cost used to be recalculated from today's hourly rate, so a pay rise quietly re-priced every rota you had already paid. Worked shifts now use the rate on their timesheet, and shifts are stamped with their rate when published.
  • Late-night shifts land in the right week - a shift starting at 23:00 and clocked in after midnight used to count as scheduled in one week and worked in the next. Both sides now follow the shift, and every date on the page is read in your company's timezone rather than the server's.
  • Open shifts are no longer invisible - unassigned shifts have no rate to cost, so they were silently missing from Insights while the rota still counted them. The page now says how many there are and how many hours they represent.
  • Shift reminders run on a schedule - the "your shift starts soon" and "don't forget to clock in" nudges used to fire only while a manager happened to have the app open. They now run every five minutes on their own.
  • A notification that fails is retried - a failed send used to disappear with no trace. It is now retried with a backoff and, if it still cannot be delivered, recorded and reported instead of lost.
  • Turn off the duplicate email - if you get push notifications you can now stop the matching email from your profile. If you have no push notifications you keep receiving email, so you never miss a shift change.
  • A phone layout that fits the screen - a tab bar at the bottom of every page, filters behind a single button, wide tables replaced by card lists on Timesheets and Insights, Back moved into the header, and empty dashboard cards collapsed to a single line.

Changes

Features

  • feat(mobile): bottom tab bar with the sections for your role, a bottom-sheet filter control reused across Timesheets, Insights and Company costs, and Back moved into the header on a phone.
  • feat(timesheets): card list instead of a 980px-wide table below sm, with the same row actions.
  • feat(insights): card list with a sort control, a compact summary strip in place of three stacked cards, and labels explaining the saved range, the four-week trend and what "include pending" counts.
  • feat(insights): staff with no activity in the period are listed as zero rows instead of being absent, and hours clocked against no shift are marked as unscheduled.
  • feat(rota): shifts are stamped with the hourly rate they were published at, and re-stamped if a published shift moves to another day.
  • feat(notifications): per-user email preference with a toggle on the profile page, retries with a backoff for failed sends, and republishing a week no longer re-notifies everyone who was already told.
  • feat(dashboard): empty cards collapse to a single line that expands on tap, and a refresh control sits next to the title on a phone.

Fixes

  • fix(insights): variance is measured against shifts that have ended while a period is still running; range boundaries, week numbers and labels use the company timezone; scheduled and actual hours are windowed on the same timestamp; open shifts are reported separately.
  • fix(notifications): both scheduled jobs are registered, so reminders and queued notifications no longer depend on someone using the app; push to an unconfigured device is recorded as a stub rather than a delivery; emails escape the text typed into shift notes and cancellation reasons, and render times in the company timezone.
  • fix(notifications): a notification that cannot be recorded no longer fails the action that raised it - creating a shift succeeds even if the notification does not.
  • fix(mobile): touch targets meet the 44px minimum on touch screens, and a destructive Delete no longer sits a thumb-width from Edit on the costs list.

Maintenance

  • chore(notifications): read and long-dead notification events are cleaned up on a schedule, unused push subscriptions are pruned, and the cron secret is compared in constant time.
  • chore(db): repaired the migration snapshot chain, which had been unable to generate a migration since 0029.