Changelog

What we shipped, when. Newest first.

Update — 2026-07-04

4 July 2026

4 July 2026

Team Sports — first foundations (behind a flag)

Groundwork for a new Team Sports area: run a squad that plays fixtures against opponents across a season, let families say whether their child is available, pick a team, and record the result — all without touching classes, the register or gradings. This first slice adds team and fixture management, a tap-to-pick selection board, touchline result entry, a season report card (played / won / drawn / lost, availability rate, appearances), and a parent-facing fixtures list with one-tap availability. It stays completely hidden until enabled per program in Settings → Modules, and it works for any team sport — the wording and any sport-specific stats are configuration, never baked in. More to come.

Portal shop polish + safer removals

Three small fixes from a full review of the parent portal on phones and desktops: the basket bar no longer hides behind the bottom navigation on phones, the shop's loading placeholder now matches the real layout (no more content jump), and an order whose payment window has lapsed now explains that the reservation expired and to order again, instead of sitting silently on "Awaiting payment". For staff, deleting a household and removing a contact now use the standard confirmation dialog — with the action spelled out and any problem shown inside the dialog — instead of a plain browser pop-up.

A cleaner main menu on desktop

On larger screens the main menu is now a wider "command centre" panel: the same links, organised into clear groups — Daily Operations, Members, Communications, Money, Development and Settings — each with an icon, laid out so everything fits on a laptop screen without scrolling. It's fully keyboard-friendly (Tab moves through the links, Escape closes and puts you back where you were). On phones and tablets the menu is exactly as before, and nothing has moved: every link goes to the same place and the same people see the same items.

Certificates: your own fonts, Oxanium, and grade number tags

Certificate templates get three upgrades. Oxanium joins Helvetica, Times and Courier as a built-in font — it's bundled into the app, so the editor preview and the printed PDF match exactly and nothing is fetched from the internet when certificates generate. Custom fonts: Settings → Certificates now has a Fonts section where you can upload your club's own TrueType (.ttf) font — it appears in the editor's font picker, previews live on the canvas, and is embedded into every generated PDF so certificates print identically anywhere. Fonts are private to your club, can be archived safely (templates that still use one fall back and show a clear warning rather than quietly restyling), and uploads are checked server-side so a broken file can never jam certificate generation — if a font ever fails at print time, that certificate falls back to a standard font and the batch still comes out. New template tags: {grade_number} (e.g. 9), {grade_type} (KUP or DAN) and {grade_number_type} (9 KUP) — taken straight from the belt's own grade settings, rendering blank when a belt has no numbered grade. All existing templates and tags carry on unchanged.

Follow-up fixes: the older {grade_awarded_full} tag no longer assumes "Kup" — a 1st Dan certificate now reads "… – 1st Dan" (Kup certificates are unchanged, and the grade word comes from each belt's own settings, so any club vertical gets its own term). The Fonts section also now shows a clean empty state rather than an error if the database update behind it hasn't been applied yet, and .otf uploads are labelled accurately (.otf works when it contains TrueType outlines; CFF/WOFF still need exporting to .ttf).

Grading day sheet: pass students in bulk, with one confirmation

Recording a grading day used to mean tapping Pass or Unsuccessful on every student one at a time, with the sheet reloading after each tap. The day sheet now has tick boxes: tick the students who are ready (or use Select all to grade), press Review outcomes, and check the full list in one place — every line shows who's being promoted from what to what, and you can flip any line to Unsuccessful, change the awarded belt, or remove it before anything is saved. Accept then records the lot in one go, or Back and amend returns to the sheet with your ticks kept. Students your club's own policy blocks from grading (expired insurance or licence) are never swept up by Select all — they can still be ticked individually, and the review list spells out the block so including one is always a deliberate choice. If any outcome can't be saved, the ones that worked are recorded and the review list stays open showing exactly which students still need doing — nothing is silently lost.

Grading day sheet: steadier rows, correct "from" belt

Two smaller fixes from a real grading day. The From → Belt → To display now sits in fixed columns, so the arrows line up down the whole sheet instead of drifting left and right with each student's name length; the "lessons since last graded" chip has been removed from this screen to reduce noise. And after recording a pass, a graded row now shows the belt the student actually graded from (e.g. White Belt → Yellow Tag) — previously it showed the new belt on both sides of the arrow, because the student had already been promoted by the time the row refreshed.

Double promotions recorded on the day sheet now stick

If the panel changed the awarded belt in the confirm step — a double promotion, or a booking that was targeted at a specific belt — the day sheet wasn't passing that choice to the server in the way the server expects, so the student was quietly promoted to the plain next belt instead. The confirmed belt on screen is now exactly what gets awarded, on both the single-event and whole-session day sheets, and saving a panel note on an already-graded row can no longer disturb the recorded award.

Courses: a built-in learning management module

Clubs can now build and share courses with members — a new optional module (off by default; switch it on under Settings → Modules). Staff create a course, organise it into sections and lessons (text, an embedded video, a file, or a quiz), then publish it. Access is granted per member: assign or comp a course to a student and it appears in their portal; remove the grant and it disappears again straight away. A per-course dashboard shows who's been assigned and how far each member has got.

For members, the portal gains a Courses area: work through each lesson, mark it complete, and take quizzes that are marked automatically — the correct answers stay on the server and are never sent to the device. A course counts as finished once every required lesson (and its quizzes) is done, and members can pick up where they left off. Membership-plan inclusion and completion certificates are designed in and will follow; for now courses are assigned manually.

Training pathways: development tracks for members and staff

A new Training Pathways area (off by default; an owner or admin switches it on in Settings → Modules) lets a club run structured development tracks — an assistant-coach induction, a volunteer or junior-leader programme, an officials ladder, a safeguarding-role checklist. Build a pathway from ordered stages, each holding tasks with an evidence type: a simple checkbox the person marks themselves, a written reflection, an off-platform certificate reference, or a staff sign-off. Publish it, then assign it to a member or a staff volunteer — each assignment takes its own snapshot, so editing the template later never rewrites someone's in-flight track.

Staff get a progress board per assignment: every task's state, the person's reflection, a feedback box, and Sign off / Return actions (who can sign off is role-controlled). A pathway completes automatically once every required task is signed off — optional tasks never block — and a "Pathways" tab on each member's profile shows their tracks and progress at a glance.

Members see a Pathways area in the portal: each track as a card with a progress bar and a "Next: …" line, and a detail view where they can tick off checkbox tasks, write reflections, and record certificates. Staff-only notes are never sent to the portal. Training pathways are kept completely separate from grading — finishing a pathway never changes a belt or grade, and a grading never advances a pathway. Certificates on completion, course-linked tasks, reminders and bulk assignment are designed in and will follow.

Update — 2026-07-03

3 July 2026

2026-07-03

The parent portal gets a Pro Shop, club announcements, and useful links

Three additions for families, all managed from Settings. Pro Shop: clubs can now sell kit and merchandise to their members — create products with variants (sizes, colours), set prices and stock, and choose who can see each product (all members, specific programmes, or specific levels). Parents browse the shop in the portal, pay by card exactly like any other club payment (money goes straight to the club's own Stripe account), and see their order history. Stock is reserved during checkout so two families can't buy the last hoodie at once, and unfinished checkouts release their stock automatically after 30 minutes. Staff get an orders dashboard — filter, search, move orders through processing → ready for collection → fulfilled — and low-stock warnings. Parents can request a refund with a reason; staff approve or decline, and the money itself is only ever refunded through Stripe directly, with the order updating automatically when that happens. A new Product Sales report shows revenue by product and variant, kept fully separate from membership fees. Announcements: a message strip across every portal page — one message shows as a notice, several as a gentle ticker that pauses when you hover and stays still for anyone who prefers reduced motion. Useful links: a club-managed list of helpful links on the portal home page. And a full portal check-up fixed five pages that used to go silently blank when a request failed — including the insurance page — plus a long-standing annoyance: a flaky connection no longer signs parents out; the portal now says "Can't reach the club right now — you haven't been signed out" and offers to try again.

Payments now leave a visible trail, and asking "are you sure?" looks the same everywhere

A platform reliability batch. Payments: every card and Direct Debit payment now writes its life story to a permanent ledger — payment started, succeeded, failed, refunded, disputed, and any mismatch the nightly reconciliation finds — without changing a single payment behaviour; operators see the last 24 hours of it on the platform health page. The platform health page itself was reorganised into clear sections (Messaging, Payments, Automations, Cron, Configuration, Tenants), each with a status chip and a "Needs attention" summary that says what failed, when, for which provider, and what to do next — and a data source that can't be read now shows as degraded instead of pretending all is well. Support gets a first cockpit: platform operators can look up a family by name, email, invoice or booking and see their communications (with delivery history), invoices, bookings and automation runs in one place — read-only, and invisible to anyone who isn't a platform operator. Clubs get a plain-English health card on Settings → Deliverability: "Email sending is ready", "Telegram is not connected", "Real sending is off — messages are simulated" — with no internal technical details leaked. And the last native browser pop-ups in the event form, message composer, API tokens, snippets and staff screens were replaced with the app's own clear confirmation dialogs — including a proper warning before a price change on an event that already has paid bookings (existing bookings are never charged the difference), and a "Replace my draft / Keep my draft" choice before a template overwrites typed content. Failures keep the dialog open with the reason; nothing you typed is ever lost.

You can now see what happened to every email after you pressed send

Email visibility used to stop at "sent". Every message now keeps a per-recipient delivery history: open a send in Communications (or an automation run) and each recipient shows their own timeline — sent, delivered, delayed (the receiving server asked us to retry), opened, link clicked, bounced (with the reason), or marked as spam. Statuses can never move backwards, and a provider re-sending us the same notification can't duplicate history. Recipients the app deliberately skipped are now labelled honestly too — "skipped — suppressed" or "skipped — unsubscribed" with a pointer to Settings → Deliverability — instead of looking like the email went out (one message even wrongly blamed the demo mode; fixed). And a new Domain & authentication panel on Settings → Deliverability shows whether your sending domain is verified (SPF/DKIM/DMARC), whether open/click tracking is on, and whether delivery tracking is wired up — with plain-English warnings when something needs attention.

Programme filters count everyone again, and forms fail safely

Three follow-ups from the audit decisions. Filtering the dashboard or a report by programme used to show 0 members for clubs set up before the newer enrolment model — those views now fall back to the programme's belt-system assignment, so the numbers match the "Active Students by Programme" card while the enrolment model is back-filled. On public forms, a question type the app doesn't recognise no longer renders as a label with nothing to type in: it clearly says the question isn't supported online and, if the question is required, the form can't be submitted with it unanswered — a clear failure beats a wrong medical or consent answer. And the password reset page no longer spins forever on a bad link: a link with no code is called out immediately, a stuck verification gives up after 12 seconds, and either way you get a calm explanation and a button to request a fresh link.

Cleaner settings, honest errors, and one way of asking "are you sure?"

A consistency pass from the product audit. The Automations settings page now speaks to club owners in plain language — the internal platform maintenance schedule and raw technical switches it used to print are reserved for platform operators, while your club's own readiness view (channels, send safety, warnings) is unchanged. The Charges page now shows every charge, whatever category it was saved under — charges in older or imported categories used to be silently invisible and impossible to edit. Pages that hit a database problem (Club Details, the platform overview) now say so in plain English instead of printing raw database text. Saving an event now shows problems in the form itself rather than a browser pop-up, and cancelling a scheduled message opens the same clear confirmation window used for money actions — stating exactly what will and won't happen — instead of a bare browser prompt. The demo's "writes are disabled" notice now reads the same everywhere.

Payment links and other family-facing links now open properly

A routing gap meant several links meant for families and visitors bounced to the staff sign-in screen instead of the page they were promised: invoice "Pay now" links, competition entry and cancellation links, the public waiting-list signup page, the Telegram opt-out link, the return page after setting up a Direct Debit, and the "thanks, you're enrolled" page. All of them now open directly — no staff account needed — while every link still checks its own secure token before showing anything. The staff Add Student wizard, which could get stuck on "Preparing Wizard…", now loads properly too, and the changelog page opens for visitors from the website footer. A permanent automated check now keeps the public-page list from drifting again.

The app now behaves properly on phones and tablets

Four layout fixes from the audit. "+ Event" works on phones again — a page animation was quietly breaking every pop-up dialog opened from the main content area (event forms, closure forms, series and bulk-cancel dialogs), leaving them stranded thousands of pixels below the screen; one CSS correction fixes them all. On a household's invoice list, the Mark Paid / Credit / Write off / PDF buttons no longer spill off the edge of a phone screen — they wrap neatly onto the next line — and a hidden markup problem that made React complain on every household page is gone. Tablet-portrait horizontal scrolling is fixed everywhere: the top bar was slightly too wide at 768px, nudging every single page into a sideways wobble; the search box and two lower-priority icons now wait for wider screens (they stay reachable from the menu drawer). And the platform admin and billing pages show one header instead of two — they no longer stack the normal staff navigation on top of their own.

Broken pages fixed, and failures now say so instead of pretending

Five reliability fixes from the full product audit. The competition (external event) detail page and the Progression report were both broken by queries against mis-named database columns — the competition page failed outright, and the Progression report loaded as a convincing "all clear" (0 promotions, nobody overdue) when it had actually failed. Both now load correctly, the report shows a clear "couldn't load — try again" message if anything goes wrong, and an automated check now scans the whole codebase for that same class of column mistake (it caught and fixed two more on the way: empty rank pickers on the event manage page and a silent rank lookup in Assess). On the register, a tap that the server rejected used to look saved — the button lit up and the on-mat count went up anyway. It now shows an error and puts the register back the way it was. And on the waitlist claim page, a payment set-up failure used to leave families staring at "Preparing payment…" forever; it now explains what happened, confirms nothing was charged, and offers the button again. The grading day sheet also now distinguishes "this session doesn't exist" from "loading failed — try again".

Waiting-list controls now go through the same safety checks as every other setting

The per-class waiting-list controls (turning a class's list on or off, setting its capacity, choosing how offers are made, removing someone from the queue, and adding someone by hand) used to write to the database directly from the browser. They now go through the same guarded server path as the rest of Settings: only staff with the settings permission can change them, the demo club properly says "writes are disabled here" instead of quietly saving, and the manage link for a hand-added entry is generated on the server. If a change can't be saved, the page tells you why — nothing pretends to have worked.

Money actions now ask properly before they act

Anything that moves money or changes a member's status now gets a clear, deliberate confirmation — no more one-click surprises or bare browser pop-ups. Mark Paid, Refund, Undo payment and invoice edits open a proper window that states exactly what will happen ("Record a manual payment of £25 for Sam — no card is charged"), whether it can be undone, and asks for a reason where one is recorded. Cancelling a trial now confirms first — naming the child, date and class — and is honest that no message goes to the family automatically. Household credit and write-off amounts get a real form with validation instead of a browser prompt box, and the button tells you the exact amount before you press it ("Credit £5.00"). If the server rejects anything, the window stays open with the reason — nothing is lost and nothing pretends to have worked.

The app now speaks your club's language

Allsorted grew up around martial arts, and it showed: tooltips, examples, report columns and page headings said "belts", "gradings" and "students" even for dance schools, swim clubs and football academies. That's fixed across the whole app. Screens now use each club's own terminology — a gymnastics club sees "Gymnasts", "Levels" and "Assessments" where a martial-arts club sees "Students", "Belts" and "Gradings" — and shared examples read for every club type ("e.g. Gum shield, swim cap, ballet shoes"). The privacy policy no longer describes every club as a martial arts club, the writing assistant stops assuming martial arts when drafting messages, and the parent portal uses the club's words too. Existing martial-arts clubs see exactly the wording they had before — nothing changes unless a club's activity type says it should.

Table actions now work the same way everywhere (first wave)

Every list used to do actions its own way — some buttons appeared on hover, some sat inline, some hid in different menus. The app now has one house pattern: each row keeps at most one main action button (Edit, Publish, Retry), and everything else lives in a consistent ⋯ menu at the end of the row — same position, same look, destructive options always last and always asking before they act. The menus open in available space (never off-screen), work fully from the keyboard, and rows never resize or jump when you hover. The first wave covers automations, tasks, scheduled changes, the communications send history, and the venues, memberships, timetable and drill-library settings tables — with every action doing exactly what it did before. More screens follow in later waves.

Table actions: second wave

The consistent row-actions pattern now reaches more screens. On an event's Invites tab, Resend stays the one-tap button and History + Revoke move into the row's ⋯ menu (Revoke still asks first, with the same wording). The Staff settings list gets the same treatment: Edit stays up front, and Resend invite, Reset MFA and Remove sit in the ⋯ menu — with exactly the same confirmation steps as before. Event types in settings now match the other settings tables (Edit up front, Delete in the menu, reorder arrows unchanged), and their actions no longer hide from keyboard users. The External events list is now fully keyboard-navigable — Tab to a row and press Enter to open it — and event rosters, reports and the comms map all use the same subtle row highlight. The event Bookings tab keeps its buttons exactly as they are (they touch payments, so they change in a later, carefully-reviewed wave), but its rows no longer shift about as statuses change.

The platform health page now shows payment-webhook trouble, not just job trouble

The internal health page used to tell you *that* scheduled jobs ran and *how many* webhook deliveries failed — but not what failed or what the recovery sweeps actually caught. It now surfaces the newest failure signals directly:

  • Card-payment webhooks get their own headline tile and section: how many

deliveries failed in the last 24 hours, how many have been stuck "in progress" for over an hour (the crashed-mid-flight case the new durable ledger exists to catch), and a table of the failed deliveries themselves — which event, what the error said, and when it arrived. On a database that hasn't received the ledger upgrade yet, the section says so plainly ("ledger not yet migrated") instead of showing a falsely reassuring zero.

  • Direct-debit webhook failures now show the same detail table — operators see

*what* failed, not just a count. (The count itself was quietly broken by a wrong column name and always read zero; that's fixed.)

  • Recovery sweep counters: the latest run of the two safety-net jobs — the one

that re-schedules automation waits the external scheduler dropped, and the one that reconciles provider payments against the ledger — now shows its numbers on the page: missed wake-ups and reclaimed stuck jobs, payments checked, new findings and auto-resolved findings, with anything above zero on the "the primary path dropped something" counters highlighted in red.

The page stays strictly read-only, and any single section that can't load shows a small "query failed" note while the rest of the page carries on.

The Comms Trigger Map now shows every automation, not just the old ones

The Trigger Map (Comms Map → Trigger Map) used to only know about flows built in the older Settings → Comms Flows editor — anything set up in the newer Automations builder was invisible here, even when it was live and sending messages, which made the map's count of active flows misleadingly low. It now shows both: each lane carries a small label — "Legacy flow" for the older editor, "Builder" for the new one, or "Scheduled" for a builder automation that runs on a schedule rather than an event — so it's always clear which editor owns it and where to go to make changes. The active flow count and the category/status filters now count both kinds together.

What's On now shows your child's own classes, not just things you can book

The parent portal's What's On calendar used to only show sessions you could book — if your child's regular class wasn't one you needed to book separately, it never appeared, so there was no easy way to answer "when is training this week?" from the calendar. It now also shows My Classes: your child's own enrolled recurring classes, marked in amber on the month grid and clearly separate from bookable Sessions (blue), Events (green) and Competitions (indigo). Tap a day to see the class name, time, venue and which child it's for. Nothing about booking, cancelling or paying for anything has changed — this is purely an extra read-only view of classes you're already enrolled in.

Update — 2026-07-02

2 July 2026

2026-07-02

The student profile looks finished at every window size

On wide screens (or zoomed out), the coloured wash at the top of a student's profile used to stop at a fixed width, leaving bare strips either side that made the page look unfinished. The background now runs edge-to-edge at any window size while the profile content stays comfortably centred — and nothing about the mobile layout changed. A sweep confirmed no other screen shares the old pattern.

Cancel many event bookings at once — with or without refunds

Emptying an event used to mean cancelling every booking one by one. The event manage page's Bookings tab now has checkboxes on the booked and attended rows and a Cancel selected button: tick the people to cancel (or select all), confirm, and the whole batch is processed in one go. Confirmation happens in a proper window rather than a browser pop-up: it shows how many bookings are selected, how many are paid and how many are free, and reminds you that waitlist spot offers won't be sent during a bulk cancel. If any of the selected bookings are paid, you choose up front whether refunds go out — the default matches cancelling a single booking (refunds are issued automatically), and cancelling without refunds is always an explicit choice, never an accident: it's a separate option, and you must also tick an "I understand paid bookings will remain paid and no refund will be issued" acknowledgement before the button — which spells out exactly what will happen, e.g. *"Cancel 8 bookings and issue 3 refunds"* — will fire.

Each booking is handled individually and honestly: one that can't be cancelled (or whose refund hits a problem) never blocks the rest, and the result tells you exactly what happened — "7 cancelled (5 refunded) · 1 skipped", with a named message for anyone whose refund or invoice needs a follow-up. Refunds are spaced out behind the scenes so a big paid event cancels reliably, and the same double-refund protection as single cancels applies: retrying a booking can never refund the same payment twice. One deliberate difference from cancelling a single booking: a bulk cancel does not email the waitlist that spots have opened up — when you're clearing an event, those seats aren't really becoming available. The cancelled members themselves still get their normal cancellation messages.

Windows and menus appear reliably on top (first wave)

Dialogs, drawers and menus now sit on one carefully ordered layering system, and the first wave of screens has moved onto it: the main app's student profile, household, settings and key event/calendar dialogs. Most visibly: opening a dialog from a student's profile (editing contact details, changing a plan, confirming a status change, the belt history, and friends) could previously render behind the student name list on the left, leaving it half-covered and seemingly broken. Every one of those dialogs now reliably appears on top, along with the equivalent windows on the households, events and settings screens. On the migrated screens the order is guaranteed: menus float above panels, dialogs above menus, a confirmation opened from a dialog above that dialog, the security timeout above everything except toasts — which always win. Nothing about what the windows *do* has changed; they just show up where you can see them.

Not every screen is on the new system yet: a number of older dialogs — the register screen's overlays, the grading and external-events windows, some communications pop-ups, and the public sign-up and member-portal pages — still use the previous layering. They behave correctly on their own pages today and will be brought across in later waves (the calendar's closure form, the trial-embed window and the voice-note editor came across with this release). An automated check now stops badly layered windows sneaking back into the new system; it polices the new-style layer values, so the older screens stay on a tracked backlog rather than under the check.

A second batch follows in the same release: the new-task dialog, the automations "how would you like to build this?" chooser, the communications template picker and its test-send / confirm-send / invite-to-event windows, the staff trial-booking dialog, the password-confirmation gate used for sensitive undo actions, the add-student wizard's saving screen and duplicate-email warning, and the video-review flag/archive/delete confirmations. As always, nothing about what these windows *do* has changed — same buttons, same behaviour — they simply can never be trapped behind other parts of the page any more. The communications preview and history side panels deliberately stay as slide-over drawers; that's their correct layer, and the automated checks now assert it.

A database hiccup no longer masquerades as "not found"

Across nine more areas — saving a student signature, changing a student's status, student achievements, household details and contacts, communication flow steps, email template versions, lesson drill tags and lesson plan feedback — a temporary database problem during the ownership check used to come back as a flat "not found", exactly as if the record didn't exist. Staff would retry, assume the record was gone, or file a confusing bug. Those checks now tell the truth: a record that genuinely doesn't exist (or belongs to another club) still says "not found", word for word as before, but an infrastructure error now surfaces as a proper server error that's visible in monitoring — so a wobble can't silently pretend your data has vanished. 114 automated tests pin the exact wording and behaviour of every one of these responses.

Two people editing the same thing no longer overwrite each other

If two staff members have the same record open — an automation draft, or an event's details — the second save used to silently wipe out whatever the first person had just changed, with nobody the wiser. The app now notices the record was changed since you opened it and warns instead of overwriting: *"This was changed elsewhere. Refresh to see the latest, then re-apply your edit."* Nothing you typed is lost — your form stays on screen, so you can check the latest version and apply your change again. This protection covers the automation builder's Save draft and event editing from the calendar first, with more screens to follow. (Rescheduling an event that already has bookings takes a separate path that isn't covered yet.) The same protection now also covers three settings screens: editing an event type, editing a class on the timetable, and editing an automation category.

Automations know exactly who they're talking to

Owner and admin Telegram alerts are now resolved strictly per club — your bot token and your admin recipient list, nothing shared, no hidden fallback. And if an automation asks to notify the owner before Telegram is set up (no bot connected, or no admin recipients added under Settings → Club Details → Telegram Integration), the step no longer pretends it succeeded: the run log shows a clear "owner Telegram not configured" skip, and the rest of the automation's actions carry on regardless. The "Member + Admin" copy on a Telegram step now records whether the admin copy actually went out, too.

Emails about a member can now be addressed to the household bill payer — the household's billing address, or its primary contact — which is usually the right inbox for payment reminders about a child. When no payer can be resolved (no household, or no address on file), the message falls back to the member's own contact and the run log records exactly why. Test mode is unchanged and watertight: every address, including a payer's, obeys the same live/production/test-allowlist rules on every path — including reminders that wake from a schedule.

Automations → Upcoming has grown up. Every durable scheduled job — a paused "wait 2 days" step or a timed trial/event reminder — now shows who it's about ("Trial — Sam Smith, 17/07 18:00", "Event — Autumn Camp — Ava Evans"), who will receive it, what it will do (email, SMS, task…), exactly when, and which automation (and version) created it. Jobs whose wake-up is held by the external scheduler carry a small badge so you can see how they'll fire.

It's no longer just the future, either: new filters let you flip between what's still Scheduled, what already Fired, what was Cancelled or Superseded (say, after a reschedule — with the reason shown right on the row), and what Failed or Skipped, plus a trigger-type filter and a due-date range. The default view is unchanged: what's due to go out next, soonest first.

Cancelling or rescheduling now tidies up its reminders

When a trial or event is cancelled, its pending timed reminders are retired on the spot — not left to quietly expire. When one is rescheduled, the old reminders are marked superseded and fresh ones are scheduled against the new start time automatically. And when you publish a reminder automation with a timing, it now also picks up bookings that already exist — so "2 hours before" applies to everyone, not just people who book after you press publish. If a reminder's background job can't be reached to cancel, no matter: the database is the authority, and a stale wake-up always stands down without sending. Any genuine database error at send time now fails loudly (visible in monitoring) rather than being mistaken for "booking gone".

Timed trial and event reminders — "2 hours before start"

Trial and event reminder automations gain a "Send when?" control: keep the default day-before reminder, or choose an exact offset — say, 2 hours before the trial starts. Timed reminders are scheduled the moment the booking is made, anchored to the real start time (not a blind delay), and re-check everything before sending: the booking still stands, the event isn't cancelled, the start time hasn't moved, the automation is still published and the club still has automations on. If any of that changed, the reminder quietly stands down instead of sending something stale. Each automation owns its reminder exactly once — a timed automation is skipped by the old daily sweep, so there's no double-send path.

The builder also now refuses impossible condition combinations at publish (e.g. using a date window on a text field), so imported or hand-edited automations can't sneak past validation.

Scheduled automation waits are now durable

Behind the scenes, every scheduled automation wait (the "wait 2 days then continue" steps) is now always recorded in the database before any external scheduler is involved, so a configuration change mid-wait can no longer lose a pending automation. When a wait wakes up it also re-checks that the club still has automations enabled (and isn't the demo club) before sending anything — a long wait can't outlive a decision to switch automations off. A light daily tidy-up job recovers anything that gets stuck.

Dropdowns and popovers now stay on screen

Small anchored menus — the equipment checklist on a grading event row, the ⋯ actions menu in the automations table, the timetable Export menu — used to open blindly downwards. Near the bottom of the window they could run off-screen or stretch the page with an extra scrollbar, and inside scrolling tables they could be clipped mid-menu.

They now share one smarter popup system: each menu opens above or below its button depending on where there's room, slides in from the button rather than popping from nowhere, never crosses the edge of the screen, scrolls internally when space is tight, and follows its button when you scroll. Escape or a click elsewhere closes them, and keyboard focus lands back on the button you started from. Everything the menus *do* — ticking checklist items, archiving automations, exporting PDFs — is unchanged.

A second wave brings the everyday header and comms dropdowns onto the same system: the top-bar navigation menus, the club switcher, the program lens, the little "i" info popovers on settings headings, and the Test SMS / Test Telegram popovers on the bulk-send page. Same upgrade, same promise: what each menu does hasn't changed, it just can't fall off the screen any more.

A fourth wave covers search-as-you-type and the last everyday menus: the register's add-student search, the task screen's student search (including inside the new-task window), the tasks mobile action and status menus, the student list's bulk status menu, the household "+ Add Member" menu, and the grading rubric's icon picker. Search lists now follow their box, flip upward near the bottom of the screen, support full keyboard navigation (arrow keys, Enter to pick, Escape to close), and announce themselves properly to screen readers.

A third wave finishes the job on the busiest screens: the Sort panel and the Send-options and Add-student menus on the Students page, the Sort panel on the Communications recipient sidebar, the "+ Insert tag" picker in the event editor (which now reliably sits on top of the event window instead of hiding behind it), and the Insert form link / Insert snippet pickers in the email composer. As before, everything these menus *do* — sorting, sending forms, adding students, inserting tags and snippets — is exactly as it was; they just position themselves properly, close on Escape or a click elsewhere, and hand focus back to the button that opened them. A new automated check now stands guard so old-style, clippable dropdowns can't sneak back into the app.

Waitlist promotions get their timed reminders too

Timed event reminders ("2 hours before start") were scheduled when a booking was first made — but a member who reached "booked" from the waitlist slipped past that moment. Now every route to a seat schedules the same reminders: a staff promotion from the waitlist, a member claiming a freed spot on a free event, and a paid claim the instant the payment is confirmed and the seat is actually won. Retries can't create duplicates, and a reminder hiccup can never interfere with the promotion or the payment itself.

The automation builder now speaks plain English

Simple setup has had a polish pass so building an automation reads like a sentence: when this happens → only continue if… → then do this. The conditions section stays out of the way until you actually add one, and everything the builder says about your automation — the plain-English summary, the preview chips, each step's one-liner — now uses friendly wording throughout ("Previous status is Active", never raw internal field names).

The Annual requirement due trigger gets the same treatment the status-change trigger already had: instead of a technical field/operator/value row, you set a simple "Expires within [30] days" window — and the summary reads exactly that, e.g. "expires within 30 days" for an insurance renewal chase. It's stored as an ordinary condition underneath, so anything built before (or anything more unusual) still opens in the familiar condition editor with nothing hidden.

Email steps also gain a Send to choice: the member's own contact (as always), or the household payer — the right inbox for payment and renewal emails about a child. If no payer address is on file, the message safely falls back to the member's contact and the run log says why. And timed reminders now spell out their one honest edge case: someone who books *after* the reminder's send time has already passed won't get that reminder.

Going back from an event returns you to the day you came from

Opening an event from a calendar day and clicking "Calendar" used to drop you back at the calendar root, losing the day you were working through. The event page's breadcrumb now reads Calendar › the event's day › event name, and the new middle link takes you straight back to that day's view. It's worked out from the event itself, so it still points at the right day after a refresh or when following a shared link.

Coach notes, session feedback and incident reports survive bad venue signal too

The same keep-and-retry protection that assessment comments gained now covers two more places staff type things at the venue: coach handover notes on a session's register card, and the session feedback form. If the save can't reach the server, the card shows an amber "waiting to send — will retry when you're back online" note, keeps your text exactly as typed, and sends it the moment the connection returns (or on Retry now). Retries can never save a duplicate or an older version over a newer one.

The two places this mattered most are now covered too: notes typed on a grading day. The day panel's booking notes used to be able to vanish silently if the connection dropped at the wrong moment — the save looked fine but never happened. And an examiner's judging notes could actually be erased from the screen after a second of bad signal. Both now keep your text exactly as typed, show the same amber "waiting to send" state, and send the moment the connection returns — an examiner's note even survives reloading the page mid-outage. Scores themselves behave exactly as before.

The accident/incident form gets a different, equally important safety net: everything you type into the report now autosaves to this device as you go. If the page reloads or the tab closes mid-report — exactly the moment that tends to happen during a real incident — reopening the form offers to restore your draft ("Draft restored from this device"), which you can keep or discard. Submitting still happens in one deliberate step, exactly as before, and clears the draft.

Assessment comments survive bad venue signal

Sports-hall Wi-Fi drops mid-session; typed comments shouldn't drop with it. On the assessment grid, a comment you write with bad signal is now kept and retried — never silently lost. If the save can't reach the server, the comment stays visible with an amber "waiting to send — will retry when you're back online" banner (and inside the technique's comment list), then sends itself the moment your connection returns — or immediately via the Retry now button. It even survives closing or reloading the tab: pending comments are stored on the device until the server confirms them. Nothing is ever marked as saved until the server actually says so, a retry can never create a duplicate comment, and a genuine rejection (say, an expired session) is shown to you straight away rather than retried forever.

Repeating events — and a quicker way to create any event

Events can now repeat properly. When you create an event, a new repeat picker offers options built from the date you chose: every X days, weekly or fortnightly on that weekday, monthly on the same date, or patterns like "monthly on the 3rd Tuesday" and "monthly on the last Friday" — with a chip preview of every date before you save, so there are no surprises. The old monthly repeat could drift at the end of a month (a 31st sliding into early the month after); the new date maths can't. Repeating events are saved as a linked series, and cancelling one now asks the honest question: just this event, or this and all future events in the series? Cancelling the future events follows the same bookings-first rule as always — any occurrence that still has active bookings is skipped, never silently cancelled, and the result tells you exactly what happened ("4 cancelled · 1 skipped — still has active bookings"), listing the dates that need a follow-up. This same choice now appears everywhere an event can be cancelled — the day view and the event's own manage page both offer it for events in a series.

Creating any event got faster too. The type picker is now a single step — every event type on one screen, grouped under its category heading — and if you only have one type, you land straight on the details. The end date stays out of the way behind a Multi-day event toggle (most events are one day; the end date simply follows the start date until you say otherwise). The form also remembers the start time you last used for each event type, and the sign-up cut-off and early-bird deadline gain one-tap presets — "1 day before", "3 days before", "1 week before" — computed from the event's own date and time, with anything that would land in the past sensibly greyed out.

Invoices for paid items now show their real line amounts everywhere

A paid invoice could show £0.00 against its line items — on the PDF a parent downloads from their confirmation email, on the public "pay this invoice" page, and in the household billing view — even though the invoice total at the bottom was right. It affected competition/external-event entries and older event-booking and enrolment invoices, and it was purely a display fault: the money taken and the totals stored were always correct. Every place an invoice renders now computes each line the same way, preferring the exactly-stored amount and otherwise deriving it from quantity × unit price — so a £20 entry fee reads as £20.00, never £0.00. New competition-entry invoices also store their line totals outright so they're right at the source.

Behind the scenes, the invoice screens are more honest when something goes wrong. Previously a database hiccup could quietly render a PDF with no line items at all, show a parent an empty "no invoices" payments page, or turn a legitimate pay link into "Invalid link". Now: the PDF says plainly that line details are temporarily unavailable (with the correct total still shown), the portal and staff billing views show a clear error instead of pretending there's nothing there, and every such failure is logged with the invoice reference so it can be chased. Promoting someone off an event waitlist also now ties the invoice it raises to that booking — so when the family pays, the payment settles that exact invoice rather than ever creating a second one.

Two follow-up polish points from the independent verification of this fix: invoice PDFs downloaded earlier the same day can no longer serve a stale pre-fix copy (the PDF cache now refreshes itself the moment the invoice rendering changes, not just when the data does), and the public pay page now distinguishes a temporary problem — "could not load this invoice right now, try again in a few minutes" — from a genuinely dead link, so a parent with a valid link is never wrongly told it's invalid during a brief outage.

Update — 2026-07-01

1 July 2026

2026-07-01

Smarter automation triggers: status transitions and expiry windows

The automation builder now understands *changes*, not just states. For "Student status changed" you pick a simple From / To (e.g. From Active → To Leaver) instead of wrestling with raw fields — and the automation reads the actual change, so it fires on the transition itself, never on whatever the record looks like afterwards. Requirement-expiry triggers gain a friendly "expires within [X] days" condition (checked against server time; a missing or invalid date simply doesn't match). Your existing automations carry on exactly as before.

Onboarding saves now fail visibly, never silently

During club setup, removing a grade type or a programme used to *look* done even if the save failed behind the scenes. Those steps (and the club details / membership plan / preset saves around them) now show a clear error and keep your data on screen when something goes wrong, so nothing quietly disappears — and remove buttons can't be double-clicked into duplicates.

Safer "request a new booking link" for event invites

When a guardian asks for a fresh event booking link — from the public booking page or when staff resend an already-used invite — the old link now stays working until the new one has actually been sent. Previously the old link could be invalidated a moment before the email was queued, so a delivery hiccup could leave the recipient with no usable link at all.

Now the flow is delivery-safe: the new link is emailed first, and only once it's safely queued does the invite swap over to the new link. If the email can't be sent, nothing changes and the original link keeps working — the guardian can simply try again. Bulk "resend invites" follows the same rule: a row whose email fails is reported as skipped rather than counted as sent, and never loses its existing link.

Booking requirements for events (checklists, waivers, info)

Events can now carry their own booking requirements — reusable blocks a parent completes before their place is confirmed:

  • Checklists — a list of things to confirm (e.g. "I have my sparring gear"), each optionally required.
  • Waivers / acknowledgements — text the parent must accept before booking.
  • Information — a read-only note shown during booking (parking, what to bring, arrival time).

Add them when editing an event, mark any as required, and the public booking form shows them under "Before booking". A place can't be confirmed until every required item is done — on both free and paid bookings — and what the parent agreed to is saved against the booking exactly as it read at the time, so later edits never rewrite historical consent. Staff who confirm a booking directly have it recorded as completed by staff.

This replaces the old, martial-arts-specific grading equipment checklist with one generic system that works for any club, event type or vertical. Existing grading events keep their equipment checklist automatically — it now simply flows through the new system.

Default booking requirements per event type

You can now set booking requirements once on an event type (Settings → Event types) and have every new event of that type start with them — no more re-adding the same checklist or waiver to each event.

  • Set them up under "Default booking requirements" when editing an event type.
  • New events of that type are created with their own copy of those requirements, ready to tweak per

event.

  • Existing events are never changed — the defaults only apply to events created afterwards, and each

event's requirements stay independently editable. Changing a type's defaults later won't rewrite events you already made.

  • Grading events still get their automatic equipment checklist on top of any custom defaults — no

duplicates.

If those defaults ever can't be copied onto a new event, the event still saves and you get a clear non-blocking notice to open the event and check its requirements — creating an event is never blocked. Deleting a default now spells out that it only affects new events of that type from now on; existing events are never changed.

Update — 2026-06-30

30 June 2026

2026-06-30

Six new automation triggers

The automation builder gains six new triggers you can build email automations on. They appear in the trigger picker alongside the existing ones (New student enrolled, Trial booked, Event booked, …), each with their own merge tags ready in the editor.

Trial triggers

  • Trial cancelled — fires when a trial booking is cancelled (by staff or via the public

cancel link). Carries the trial date, class name and the contact's details.

  • Trial rescheduled — fires when a trial is moved to a new date/time. Carries the

previous and new trial date/time, so you can send a clear "we've moved your trial" note.

  • Trial reminder — sent the day before an upcoming trial, as a gentle reminder. Runs from a

daily background job; each booking is reminded at most once.

Event triggers

  • Event booking cancelled — fires when a student's event booking is cancelled. Carries the

event name, date, time and location. (Any refund is handled by the event screen exactly as before — this trigger never touches money.)

  • Event rescheduled — fires when an event is moved, once per booked or invited student.

Carries the from date/time and the to date/time plus the new location.

  • Event reminder — sent the day before an upcoming event to every booked student. Runs from

a daily background job; each student is reminded at most once per event.

Each new trigger runs through the same safe pipeline as the existing ones: it only sends when a club has published an automation for it and the runtime is switched on, never sends on the demo club, and every send is visible in the Command Centre. Where the older notification system also covers one of these events, it now steps aside once you publish a builder automation for it — so there's no risk of a member getting the message twice.