BookYourPTO

Changelog

Stay up to date with the latest changes, improvements, and bug fixes in BookYourPTO.
Latest

v1.1.2

Highlights

Home — the page you land on after signing in

Signing in used to drop you on the Dashboard, a calendar grid built for planning team coverage. It answers "who is off when" well and almost nothing else, so anything waiting on you personally lived behind a sidebar entry you had to think to visit.

Home is now the landing page. It gathers what needs you, your own records, and — for people who manage or administer — their team and the organization. The Dashboard is unchanged and still in the sidebar; nothing was taken out of it.

What appears depends on your role, on whether anyone reports to you, and on which modules the organization has switched on:

CardWho sees it
Action Required — documents awaiting your signature, assessments you owe somebody, onboarding tasks and training past their date, expired certificationsEveryone. Cannot be switched off
Waiting on You — leave, expense and time requests routed to youAnyone requests are routed to
Time Off — your balances and your next booked leaveEveryone
Today's Hours — hours logged today and whether you are clocked inWhen time tracking is on
Next 1:1 — who you next speak to, and whenEveryone
My Stuff — goals, training, certifications, benefits and equipmentEveryone
Who's Out — who is away today, plus upcoming public holidays on your own calendarEveryone
Celebrations — birthdays and work anniversaries over the next two weeksEveryone
What's Happening — recent updates about your requests and documentsEveryone
Probation Reviews — reviews falling in the next two weeksThe employee, their manager, their department head, administrators and executives
My Team — your people, who is away, and when you next speak to eachAnyone with reports, by org chart rather than by role
Onboarding — onboarding and offboarding still runningAdministrators, executives, and department heads for their own department
Headcount, Review Cycles, Needs AttentionAdministrators and executives

Every row links to the page that shows exactly what it counts, so a count and the page behind it cannot disagree.

Arrange the board yourself

Customise turns the board into the editor. Each card gains a grip and a × on its corners, and an Add cards tray appears along the bottom. Drag a card to move it and the rest close up around it; arrow keys move the focused card too, so the board can be arranged without a mouse. Nothing is saved until you select Save, and Cancel puts it back.

The tray is not only the cards you have removed. There is a library of optional cards that start switched off, so Home does not open with twenty at once:

  • Company Holidays — the next public holidays on your own calendar.
  • Workforce Mix — headcount by division, work location or employment type.
  • Length of Service — how long people have been with the organization.
  • Profile Completeness — records missing a start date, department, job title, date of birth or mobile number, the fields leave accrual, approval routing and reminders depend on.

The last three are for administrators and executives. Each tile shows the card's own icon, so the tray says what you are about to add. Your layout is stored on your account rather than in the browser, so it follows you to another device, and a card added to the product later appears next to the cards it belongs with rather than at the bottom of a board you already arranged.

Switching a card off changes only what you see. It grants no access, and every endpoint behind a card still checks permission for itself.

Nothing on the Dashboard is missing from Home

Everything the Dashboard surfaces now has a home on the new page: upcoming absences for the next fortnight, upcoming public holidays resolved per person rather than per organization, and probation reviews. Probation visibility follows the same rule the Dashboard uses — self, their manager, their department head, administrators and executives — and that rule now lives in one place both pages read rather than being written out twice.

The org chart follows Reports To

The org chart was drawn from roles, not from the reporting structure. Department heads were handed to administrators round-robin and administrators to executives the same way, so the lines it drew were invented: the third person in a list reported to the first administrator purely because of their index. Anyone reading it as a reporting structure was being misled, and the Reports To field people had carefully filled in was ignored.

It is now built from Reports To and from nothing else:

  • An executive is no longer automatically at the top. If an executive reports to somebody — a founder, a board, a parent company — they appear beneath them. Role now affects only the order the top-level people are listed in.
  • Anyone who reports to nobody is a top-level person, so a chart can have several.
  • Somebody whose manager is deactivated, somebody recorded as their own manager, and a reporting loop each surface at the top rather than vanishing, so the record that needs fixing is visible.

Export on the chart offers a PNG of the chart as drawn, the same image as a PDF, and a .csv shaped for Visio, for Lucidchart, or unformatted for a spreadsheet. The files are built from the same Reports To field, so a download and the chart always agree.

Who can download is set separately from who can browse, under Download Access in Company Directory settings. It defaults to all employees. Somebody who cannot view the chart can never download it, whichever option is chosen — export can be narrower than viewing but never wider.


Improvements

  • The New menu — starts the things you can start: a time off request, an expense report, a time entry, an employee, a signature request, a report. Entries appear only for actions you have permission to complete, and each opens the form rather than merely landing you on the page.
  • "Other departments: Hidden" is honoured — with that setting on, Who's Out and Celebrations are limited to a non-administrator's own department, matching the Dashboard. Administrators and executives are exempt, as the setting says.
  • Private leave types read as "Private" to anyone not entitled to the detail, through the same helper the calendar and the digest emails use.
  • Who's Out sends only what it draws — a name, a date range and a leave type. The reason somebody gave for being off, their notes and any approver comment stay on the server.
  • Onboarding and offboarding runs follow the existing rule — administrators, executives, and a department head for their own department. Having somebody report to you is not enough: that a colleague is being offboarded is not something their reporting line should learn from a card.
  • Compensation and demographic breakdowns were considered and left out. Pay figures do not belong on a landing page, and a demographic split over a small team identifies individuals.
  • Balance visibility follows the organization setting — the Time Off card respects the calendar balance-display choice in General settings, so an organization that hides the sick bucket does not have it reappear on Home.
  • Deep links into create forms — ?new=1 on the calendar, expenses and users pages opens the create form on arrival and then clears itself from the URL, so a refresh or a shared link does not reopen it.
  • One landing route — where a signed-in session begins is now a single constant rather than a literal repeated across eight files, so a password sign-in, a Google sign-in, email verification and the end of first-run setup cannot land people in different places. Permission-denied redirects deliberately still go to the Dashboard: where a session starts and where somebody is bounced to are different decisions.
  • A guided tour for Home, replayable from Settings → Guided Tours.
  • Expenses and Documents cards. My Expenses shows your own claims by where they have got to — drafts, awaiting approval, approved but unpaid, sent back — with what the organization still owes you, and appears only when the expenses module is switched on. My Documents shows what is waiting on your signature, what is expiring, and what is waiting on somebody ahead of you in a signing order. Both link straight to the filtered list.
  • The first-run setup checklist has moved to Home. It was on the Dashboard, which is no longer where a signed-in session lands, so an administrator who had just created an organization had to navigate away from their landing page to find the one thing asking them to finish setting it up. It still shows only to administrators and executives, and still disappears once dismissed or completed.
  • Four more cards for the Home tray. Leave Balance gives the full picture the calendar sidebar shows — allowance, carry-over, used and remaining per bucket, with a bar for each. Deductible Leave and Non-deductible Leave split the per-type breakdown into two cards, because "what has my allowance gone on" and "what have I taken that costs me nothing" are different questions. Leave Balance follows the organization's Calendar Balance Display setting, so a bucket hidden on the calendar stays hidden here; the two breakdown cards list exactly what the calendar's Deductible and Non-deductible lists show.
  • Organization Time, for anyone whose own timezone differs from the organization's: both clocks side by side with how far apart they are right now, computed from the actual instant so it stays right across daylight saving. It is not offered at all when the two match — a second clock showing the same time is noise.
  • Home respects the Time Tracking & Projects switch. With the module off, Today's Hours no longer appears and cannot be added from the tray, and the approvals card drops its Time entries row and leaves those requests out of its total. The page now reads the organization's switch directly rather than inferring it from whether a clock request happened to succeed.
  • Download Access — a new Company Directory setting controlling who can export the org chart, independent of who can browse it. An export is a roster in a file: it leaves the product, outlives the session, and forwards like any other attachment.
  • Employee # is now an optional column on the People list, switched off by default and offered to administrators and executives. It used to be on for everyone and blank for everyone: the page reads the directory view of each person, which carried the employee number for nobody, so the column took width from the name and job title beside it and showed a dash. The directory view now carries the number for those two roles, which is what makes the column work at all. Employee numbers remain searchable for everyone, and still appear on the profile.
  • Home names what it counts. The My Expenses and My Documents cards used to give a status and a number — "Awaiting approval: 1" — and leave you to open the list to find out which. Each row now names the most recent reports or documents under it, each opening the record itself, with "and N more" when there are others. A document waiting on your signature opens straight into signing.
  • Who's Out says who. The Coming up list read "1 person away" against each day. It now lists the people whose time off begins that day, with their photo and the type of leave, drawn the same way as the people out today. Private leave types still read as "Private" to anyone not entitled to the detail, and nothing beyond a name, a date and a leave type leaves the server.
  • Work anniversaries name the organization. Celebrations read "2 years with the team"; it now reads "2 years with" followed by the organization's name, falling back to "the team" only when the organization has no name set.

Bug fixes

  • A failed load printed the server's error. When a database query failed, that meant the full column list of the User table — every field name, including the sensitive ones — rendered on the landing page in place of an explanation. Home now shows one neutral line and writes the real failure to the console, and the three endpoints behind it replace any unexpected error with a clean refusal before it leaves the server. Deliberate refusals (not signed in, not permitted) still say what they mean.
  • A department head with no department could see probation data for every employee who also had no department, because the two empty values matched each other. Both sides must now be set for the department rule to apply.
  • Celebrations could never show a photo. Birthdays and work anniversaries drew a cake or a medal where the person's face belongs, and the query behind the card fetched neither the photo nor the person's id, so no amount of styling could have fixed it. The card now shows profile photos, keeping the colour that distinguishes a birthday from an anniversary as a ring and moving the glyph to a corner badge. People without a photo still show their initials.
  • Cards were invisible in dark mode — they were painted with the page background. They now use the same card surface and border treatment as the calendar and profile pages.
  • The board collapsed into a single column while editing. The corner controls were positioned to overhang each card, and a control that overflows its column makes the browser abandon column balancing for the whole board. Cards now keep an identical layout whether or not you are editing.
  • A heading repeated the card beneath it. The board was split by section headings, one reading My Team directly above a card whose own header also read My Team, and one announcing the organization's name to the only person who already knew it. The board is now one continuous run of cards, flowing in columns so a short card sits under a tall one instead of leaving a gap.
  • Three cards could render as an empty box — a header and a border with nothing in them — when the section they summarised had no entries. They are now left out until they have something to say. Action Required still says so when you are clear, which is the point of that card.
  • The org chart invented reporting lines — see the highlight above. It is now built from the Reports To field rather than from roles.
  • The org chart could not be exported at all. It now downloads as a PNG, a PDF or one of three CSV shapes.
  • Everyone could see everyone's work anniversary. The dashboard sent each colleague's hire date to every viewer, so the whole organization saw the whole organization's anniversaries — and a department head saw into departments they have no part in. The daily and weekly digests mailed them just as widely. A work anniversary is derived from the hire date, which is employment information: it reveals tenure, relative seniority and who is new.
    It is now limited to the employee themselves, their manager, their department head and administrators and executives, on the dashboard, on Home and in both digests. Birthdays are unchanged — they carry no employment information and the year is never sent.
  • Security advisories cleared in axios, @grpc/grpc-js, devalue and dompurify. npm audit reports no vulnerabilities.
  • The greeting and the date came from different clocks. "Good afternoon" was read from the browser while the date beside it was resolved in the viewer's own timezone, so somebody whose profile says Asia/Calcutta reading from Vancouver could be greeted for an afternoon that, by the date in the same sentence, had already ended. Both now use the viewer's timezone.
  • The monthly anniversary email ignored its own rule. It narrowed to a department only when the recipient was a department head with a department assigned; a head who had not been given one yet fell through the condition and was mailed every anniversary in the organization. It now uses the same rule as the dashboard, Home and the digests, and so also reaches a head's direct reports outside their department, which it previously missed.
  • An onboarding task assigned to you opened your own profile. Action Required linked every onboarding task to your own Onboarding tab, but a task assigned to you is usually part of somebody else's onboarding and lives on their profile — so the link opened an empty tab. The card now lists one row per person, named, opening the Onboarding or Offboarding tab on their profile, which is where the onboarding notifications already pointed.
  • Time Off disagreed with the calendar. The card read each leave type's default allowance and ignored the person's own allowance and anything carried over, so somebody with 15 days plus 5 carried and 20 used saw "-10, 10 over allowance" on Home and 0 remaining on their calendar. It now shows the Annual and Sick balances the calendar shows, from the same figures.
  • My Documents counted a different set from the page it links to. "N in total" counted every signature request ever sent to you, while All documents opens your folder, which lists the documents filed to you. The two now share one definition of what a folder holds, so the number in the header is the number of documents you find when you follow the link.
  • Non-deductible Leave on Home was always empty. The card looked for non-deductible types among the deductible ones, where by definition there are none, so it said "No non-deductible leave yet" to everybody. It now lists the same types and days as the calendar's Non-deductible leave panel.
  • Deductible Leave on Home left out some leave. A type that draws down a balance without an allowance of its own — Sick Leave - Paid, typically — was dropped, so the card could read 20 days where the calendar read 22. It was also narrowed by the Calendar Balance Display setting, which the calendar's own list does not apply, so an organization showing only the annual balance lost its sick leave from the card. It now lists every deductible type with days taken, in the calendar's order, with each type's own icon, and shows a capped non-deductible type as "6 / 5 days" as the calendar does.

v1.1.1

Review cycles that actually run, 1:1s with a time and a calendar entry, and a clear answer to who sets performance up

Performance has been rebuilt around a simple split: a review cycle says when reviews fall due and who they cover, a 1:1 is the conversation itself, and the two can be linked so a review is something worked towards rather than an event that arrives.

The largest change is that review cycles now do something. A cycle was a name and a pair of dates: the scope section on the form was never saved, so a department-wide cycle came out covering everyone, and every cycle ever created was stuck in Draft because nothing in the product could open it. Cycles now run on either a shared window or each employee's own hire anniversary, cover everyone or one department or a chosen list, and send reminders to the people responsible for holding the review.

1:1s gained a time, a duration and a place in the participants' real calendars, and a 1:1 arranged by an administrator now pairs the right two people — previously it paired the administrator with whoever's profile they were standing on.

Alongside those: performance is now something an organization runs for its people rather than something people set up for themselves, destructive actions ask before they act, and several dates that shifted a day west of UTC have been fixed.

Review cycles

  • A cycle can follow each employee's hire date — Annual meant two different things depending on who set it up: a company-wide window, or a review on each person's own anniversary. One pair of date columns could not express the second at all. A cycle now chooses between specific dates — one window for everyone in scope — and employee hire date, measured from each person's own start date with no shared window.
  • A hire-date cycle is not only the one-year mark — It says how far after the start date the review falls and whether it recurs: every year on the anniversary, once N months in, or every N months. Without that, a cycle named "3 Months Probation" would have reminded on the employee's first anniversary. A 29 February start is reviewed on 28 February in years that have no 29th, and a 31st is held to the last day of a shorter month rather than spilling into the next one.
    This does not replace the probation review on a profile, which keeps its own period, its extension date and its own reminders.
  • The Type field is gone — Annual, Semi-annual, Quarterly, Probation and Custom were a label no code ever read. It implied a schedule it did not set: "Probation" connected to none of the probation machinery, and "Custom" offered nothing to customise. The schedule now says what the cycle is, and the name says what to call it.
  • The scope you choose is the scope that is saved — The form had department and employee pickers and the endpoint discarded every one of those fields, so an administrator could configure a department-wide cycle, watch it save, and get a row covering the whole company. There was no column to record a department in. Scope is now stored, the department is checked against the caller's own organization, and the list shows how many people each cycle currently covers, so a department that has been emptied does not read the same as one with thirty people in it.
  • A new cycle is running, not stuck in Draft — The create endpoint ignored the status entirely, so every cycle fell through to Draft, and no control anywhere could move it out. A cycle is now created Active, and Status is a switch on the form: Active sends reminders, Draft sends nothing.
  • "Completed" is gone, and "Ended" is worked out from the dates — Completed was a third state that no code ever set and no administrator remembered to. Whether a review is finished is a fact about one person's assessment, not about the cycle. A cycle whose end date has passed now reads as Ended without anyone marking it, and sends nothing further.
  • The question-set picker has been removed — It stored a preference that nothing ever read.

Reminders

  • Reviews are now chased at all — A cycle creates no forms and assigns no assessors, so without a reminder it was a row nobody ever heard about. A daily job now sends them, and it is scheduled in both deployment files in the same change — every reminder endpoint in this product had previously been written, tested and merged while no scheduler ever invoked it.
  • Two reminders by default, and as many as you want — 14 days ahead, when there is still time to arrange something, and 3 days ahead, when it has to be done. One reminder is either too early to act on or too late to prepare for. Administrators can choose 30 days, a week, a day, the day itself, or any other number, up to six per cycle.
  • Setting a cycle up late still produces one timely reminder — Configuring a hire-date cycle two days before somebody's anniversary means both the 14-day and 3-day marks have already passed. Sending nothing would lose the reminder entirely, and sending both would deliver two emails at once, one of them claiming the review is a fortnight away. Only the most recent milestone that has come due is sent, and it counts the days from the real dates.
  • Reminders reach the people who have to act — The employee's department head, plus executives and administrators. Where a department has no head, the employee's manager is reminded instead, so two people are not each left assuming the other owns it. The employee is not reminded about their own review; anyone matching several of these receives one email rather than one per role.
  • 1:1 reminders go out a week ahead, the day before, and on the day — A single reminder the evening before is too late to prepare for and too late to move.

1:1 meetings

  • A 1:1 has a time and a duration — Previously only a date, which meant nothing could be put in a calendar. The time is a wall-clock time in the organization's timezone, so a recurring 1:1 stays at the same hour across a daylight-saving change. Thirty minutes by default, changeable.
  • The meeting appears in Google Calendar and Outlook — One event with both people invited as attendees, so it can be accepted or declined like any other invitation, rather than two disconnected copies that cannot be declined at all. Cancelling a meeting, or deleting the series, removes the invitation from the calendars it reached — the cancellation email already said it had.
  • A 1:1 arranged for somebody pairs the right two people — The series was always built as "whoever is signed in" and the person selected. An administrator standing on an employee's profile is arranging a 1:1 for that employee, not with them, so every 1:1 set up on somebody's behalf paired the administrator with the employee and left out the manager who was meant to hold it. Left on the default, the other participant is now the employee's department head, or their manager if the department has no head.
  • Everyone with a stake is told — Both attendees, whoever arranged it, and the employee's manager. It previously notified only "the other participant", so one attendee received nothing and the arranger received nothing either.
  • A 1:1 can say which review it is preparing for — Choosing a running cycle marks the conversation as preparation for that review, and the list shows which one. A review conversation happens once, so choosing a cycle makes the meeting a single occurrence rather than a series.
  • The list says when the next one is — Rows named a person and a cadence and left the obvious question unanswered. They now read as a date and a time, with the end time too, since a calendar entry without a finish cannot be checked for clashes.
  • A 1:1 is visible to the people with a reason to see it — Its two participants, administrators and executives, the employee's manager, and the head of their department. Private notes stay visible only to whoever wrote them.

Seeing your own schedule

  • A profile now says when the next review is due — A review cycle records no assessment until one is written, so nothing about it reached the person it covered: an administrator could set up a cycle across the whole company and every profile still read "Not in a review cycle". The schedule existed only in Settings and in the reminder emails leadership received.
    The Reviews tab now opens with Coming up — the cycles covering that person and the date each one falls due, worked out from their own start date for a hire-date cycle or from the window for a fixed one. Recorded assessments follow underneath.
    Only running cycles appear, and only where the scope covers the person. A closed window, a one-off milestone that has passed, and anyone with no start date on file are left out rather than shown as overdue.

Recording a review

  • A review can be marked complete — The Coming up list said when a review was due and offered nothing to do about it. Opening one now records that it happened, who recorded it and when. Reopening clears that stamp, so the record never claims a completion that was undone.
  • Notes, with three levels of visibility — A note on a review says who may read it, named by the roles the product actually has: Employee and above, Department Head and above, or Administrator and Executive only. Each level is readable by everyone above it, and the author always sees their own. Hovering a level spells the roles out in full.
    Administrator and Executive only is how something is kept from a department head as well as from the employee. A department head writes at the first two levels for their own department, and cannot write a note their own leadership could not read.
    Visibility is stored as a level rather than a list of people, so it stays correct when somebody changes department or is promoted — a note written for "managers" is readable by whoever heads that person's department today.
  • A Review mode on the upload page — A third way to send, beside Upload to folder and Send for signature. It asks in the order the work happens: the review cycle, the document, the people who sign, and then which of them is being reviewed. The document is then openable from that person's Performance tab instead of being hunted for in the documents list.
    The first attempt put a single picker inside the Recipients panel listing every recipient crossed with every review they had — "Aiden Ramirez — FY26 Annual Review — Aug 24, 2026" and five more lines like it. That list grows multiplicatively, it put the review after the people it depends on, and the part that identifies the document, the cycle, was the part repeated rather than the part chosen. A review is a cycle plus a person, so those are now two separate questions and there is no combined list at all.
    Only cycles that are running are offered, and every review the person is scheduled for counts — a cycle's dates are derived, so an active cycle covering somebody is a real review with nothing stored against it yet. Offering only already-recorded ones left the list empty for almost everybody. The record is created as the document is sent. A cycle that does not cover the chosen person says so in words rather than going blank.
  • The employee owns the review document, whoever signs first — A review form is routinely countersigned by the department head and then the employee. The document used to belong to whoever was added as the first recipient, so listing the manager first put the form on the manager's profile and then refused the upload outright, because the review belongs to the employee. Whose review it is and who signs in what order are now separate questions: the employee owns it either way, and the signing order is yours to set as on any other document.
    With a single recipient there is nothing to decide and the answer fills itself in. Adding a second clears it rather than leaving the first person marked — which is precisely how a manager would otherwise end up recorded as the subject of their own report's review.
  • A review exists only once somebody acts on it — Dates are derived from the cycle, so there is nothing to store until a review is completed, noted or has a document attached. Creating that record is idempotent on the cycle, the person and the date, so two people acting at once get one review rather than two.

What an employee can see of their own performance

  • The Performance tab is on your own profile — Your goals, the 1:1s you attend and the reviews you are the subject of were all gated to administrators, executives and department heads, so an employee could not see a goal set for them or when their own review falls due. The tab is now on your own profile, read-only: every control that writes stays gated, and notes and comments are still filtered to what was shared with you.
    Nothing was unlocked on the server to do this. Every read already allowed your own record — only the tab was in the way.
  • An unfinished assessment is no longer readable by its subject — An assessment is created before anybody writes in it, and the assessor fills in the rating and comments before submitting. The endpoint applied no status filter, so the person being assessed could read a half-written candid rating of themselves while its author was still drafting it. Only completed assessments are returned to their subject now; the assessor still sees their own work in progress.
  • Peer and upward feedback is no longer attributed to its author — A PEER or DIRECT_REPORT assessment came back to its subject with the assessor's name and id attached, which defeats the confidentiality those two kinds of review are collected under. The content is still shown; the author is not. A manager assessment keeps its attribution, which is the point of one.
    Both were pre-existing, and both became reachable through ordinary use the moment the Performance tab opened to employees — so they are fixed in the same change rather than left for the next one to find.
  • Goal comments carry a visibility level — The same three levels as review notes, so a manager can note something about a goal without it being readable by the person the goal belongs to. The control, the wording and the levels are identical to the ones on a review, on the principle that a comment about somebody is the same kind of writing wherever it is written.
    The default is Employee and above, where a review note defaults to Department Head and above. A review note is written about somebody; a goal comment is a conversation with them on a goal whose progress they own. Existing comments are all Employee and above, so no thread that was readable yesterday stopped being readable.
  • A department head can read the discussion on their own department's goals — They could already read the goals themselves, but the comments on them were restricted to administrators and executives, which is the same inconsistency the write side had.

Documents

  • Word documents and images preview instead of refusing — Filing accepts PDF, DOC, DOCX, PNG, JPG and JPEG, and the preview handled only the first of those. Everything else got "Only PDF documents are supported for preview", which reads as a fault rather than a limitation, above a Try Again button that could never succeed. A .docx now renders as pages in the browser, and an image renders as an image.
    The rendering happens in the browser, so the file never goes to a conversion service. The older binary .doc format has no browser renderer, so it says so by name and offers the download, rather than being lumped in with everything else.
  • The same click behaves the same everywhere — Opening a document from an employee's folder used to fetch the bytes and hand them to the browser, which downloaded a Word file instead of showing it, while the same document opened from the documents list previewed. Both now use the preview.
  • A damaged file says so in our own words — A truncated upload kept its PDF header, so it reached the browser's viewer and drew that viewer's black "Failed to load PDF document" panel inside the preview, with a Reload that could not help. The file is checked for both its header and its closing marker first, and a file that fails gets a plain explanation and the download button, which is the thing that actually works.

Rescheduling a 1:1

  • The date of the next 1:1 can be changed — Editing a 1:1 offered the cadence, the time and the duration, but no date, so moving one to another day meant deleting it and setting it up again — losing the agenda, the notes and the calendar entry with it. Changing the time still moves every upcoming meeting that follows the series; changing the date moves only the next one.

Who sets performance up

  • Performance is run for people, not by them — Creating a 1:1 or a goal was allowed for "you, or an administrator", which put a New 1:1 and an Add Goal button on every profile for everyone, including people arranging their own reviews. Now an executive may act for anyone including themselves; an administrator for anyone except themselves; a department head for their own department, but not for themselves; and nobody else at all.
  • A department head can finally set one up for their own people — The previous rule gave them no way to, despite being the person who actually holds those conversations.
  • The same rule now covers goal comments — Commenting on a goal was gated separately on "an administrator, or your own goal", which disagreed with that rule in three directions at once: an employee could annotate their own goal, an administrator could annotate their own, and a department head could create a goal for one of their people and then not comment on it. The Add Comment button is gated to match, as are the delete controls on 1:1s, reviews and goals. Whoever wrote a comment may always remove it.
  • A link to a person's reviews now opens their reviews — The Reviews tab is stored internally under an older name, so a link ending ?perfTab=reviews quietly opened the 1:1s tab instead. It looked like the reviews having vanished rather than like a mistyped address.
  • The employee still owns their progress — They move the percentage and the status on a goal set for them. The goal's definition — its category and weight — stays with whoever may set it, which now includes the department head.
  • Removing follows the same rule as creating — Deleting a goal, or a whole 1:1 series, is done by whoever runs the conversation. Both previously allowed the subject: an employee could delete a goal their manager had set, or a 1:1 series their department head had arranged, along with every agenda item and note on it. The buttons are gone for people who may not use them, and the endpoints refuse regardless of the buttons.

Dates and timezones

  • A 1:1 books the day the form showed — The meeting date is a calendar day, with the time of day held separately, and it was being parsed as an instant. Every 1:1 booked from a browser west of UTC landed on the day before the one displayed. With no date given, "a week from today" was also counted on the server's clock rather than the organization's.
  • Anniversaries are counted in the employee's own timezone — It is their anniversary, so the day is resolved for them rather than for the server or whoever is looking.

Everywhere else

  • Destructive actions ask first — Deleting a 1:1 series, a goal, a goal comment or an agenda item now says what is about to be lost and names it. Several of these were a single unguarded click, and the ones that did ask used the browser's own dialog, which cannot describe what is being removed and which Chrome suppresses after a few uses.
  • Lists show what is loading — Goals, reviews and 1:1s rendered their empty state while the request was still in flight, so a profile read as "this person has no goals" rather than "not loaded yet".
  • The profile page uses the full width — Matching the people and expenses pages.
  • Vendor names removed from customer-facing copy — Guided tour text named the specific AI models behind a feature. That is an implementation detail, and it dates.

Under the hood

  • Review cycle scope, anniversary dates and "is this cycle running" each have one implementation shared by the screens and the job that sends reminders, after a second copy of the last one let the settings list call a cycle Ended while the 1:1 picker still offered it.
  • The reminder job de-duplicates on a stored marker keyed by the cycle, the person, the anniversary and the milestone, so a retry, restart or overlapping run sends nothing twice.
  • The Google and Outlook adapters had each declared their own copy of the same calendar event type, and the build was silently discarding one of them.
  • The demo lockdown guard is now documented as needing to be run the way CI runs it — scoped to the branch's own changes rather than against the whole API, where new handlers were being lost in a pre-existing backlog.

v1.0.21

Invitations that lead somewhere, profile photos that actually work, and documents that close their own onboarding tasks

This release fixes two ways a person could be left stuck at the front door. Someone invited by an administrator was chased with an email asking them to verify their address, when what they needed was to set a password — and following it stranded them for good. Separately, signing a document sent by hand left the matching onboarding task open, so employees were asked to sign the same thing twice.

Profile photos also work properly for the first time: there is a way to choose and frame one, a photo appears everywhere that person is shown rather than in a handful of places, and it updates the moment it changes instead of after a page refresh or a sign-out.

Alongside those: new organizations now start with a full set of leave types and departments rather than a blank structure, an import can invite the people it creates, departments and data import are restricted to administrators, and a signature that fails validation no longer leaves the signer unable to retry.

Invitations

  • An invited employee is now chased with the right email — Two kinds of account sit in the "email not yet verified" set and they need opposite things. Someone who registered themselves chose their own password, so what is unproven is their address. Someone an administrator invited is the other way round: the address was vouched for by the person who typed it, and the password is one the system generated and never showed them.
    Invitees were being sent "Please verify your email address". It does not unblock them, and following it made things worse: verifying marked the address proven, dropped them out of the follow-up schedule for good, and still left them with no password. The real invitation expired quietly in the background. They now receive their setup link again instead, with a fresh fourteen-day token.
  • Follow-ups are timed to when someone actually needs access — Reminders counted from the day the record was created, but administrators routinely set people up weeks before they start. That meant every reminder fired, and the invitation lapsed, before the new starter's first day. Invitations are now chased on day 3, 7 and 13 counted from a week before the start date, or from the day the account was created if that is later. The last nudge lands the day before the first link expires, so it arrives while the original is still usable.
  • Former employees are never chased — Anyone whose employment end date has passed is skipped entirely, so a roster containing leavers cannot produce an email inviting them to activate an account.

Data import

  • An import can invite the people it creates — Imported employees were created with no usable password and nothing to tell them the account existed. The preview step now offers Invite these employees to set up their accounts: ticked, each person is emailed a link to choose a password and is followed up on the same schedule as any other invitation.
    It is off by default. An import is as often a historical roster — leavers, closed leave — as it is a live workforce moving across, and emailing a former employee a link to activate an account is the one outcome nobody wants. Anyone whose employment has already ended is skipped even when the box is ticked, so the file decides rather than anyone's memory of what is in it.
  • Imports no longer report failure after succeeding — Every import finished by writing an audit entry with an action the database does not recognise. The records were created, then the write was rejected, so the job was marked failed and the administrator was told the import had not worked and invited to run it again.

Documents

  • A document sent by hand now closes the onboarding task it satisfies — An administrator can send a template straight from Documents → Templates, and the same document can also be attached to an onboarding or offboarding task. Signing the one sent by hand left the task open, so the employee was asked to sign a document they had already signed.
    The machinery to close the task already existed; what was missing was the link. Only documents created by an onboarding task recorded which template they came from, so a hand-sent document was an orphan with nothing to match on. Both the one-off send and the bulk send now record it, which closes the loop in both directions: signing a hand-sent document completes the task, and a task that later needs that document reuses the one already sent instead of issuing a second copy.
    A task is only ever closed by the employee it belongs to signing their own copy. Where a document carries more than one signer — an agreement counter-signed by a manager, say — the counter-signature closes nothing for the manager, who still has their own copy to sign.
  • A signature that fails validation can now be corrected — Submitting a signature with a required field still empty marked the assignment as signed and then rejected the submission. The document was never actually signed, the onboarding task never closed, and every retry was refused with "This document has already been signed" — leaving the signer with no way forward at all. The signature is now recorded only after the submission passes validation, so a rejected attempt leaves everything as it was and can simply be retried.
  • Templates and bulk send are reachable from the upload page — "Add a document" only ever uploaded a fresh file, and the links to saved templates and bulk send existed on the documents list alone. Administrators who started from the upload page had no route onward and no sign that either feature existed, and were re-uploading files they already had as templates.

Onboarding for new organizations

  • Ten leave types instead of eight, with the right icons — A new organization was missing Sick Leave (Unpaid) and Special Event Leave, and Annual Leave carried the umbrella icon that belongs to Unpaid Leave — the two had been swapped. The full set is now Annual Leave (Vacation Time), Sick Leave (Paid and Unpaid), Working from home, Special Event Leave, Maternity, Paternity, Meeting, Compassionate and Unpaid Leave. Each requires manager approval, deducts from the correct balance, and carries the privacy setting its category calls for.
  • Six departments to start from — Sign-up left an organization with a single department named after the company itself, created only so the founding user had somewhere to belong; everything else had to be built by hand before anyone could be invited into a department. New organizations now start with Executive, Software Development, IT Operations, Finance & Accounting, Sales and Operations, each with its own colour. The placeholder becomes the Executive Department rather than sitting alongside the new ones, so the founder keeps their membership.
    Seeding only ever runs on an organization's first trip through setup, and the rewrite only touches a department still untouched since sign-up — an administrator who renames it first keeps their name, code and colour.

Profile photos

  • Choosing and framing a photo — There was no way to set a profile picture after the first-run wizard, and no way to say which part of an image to use: whatever was uploaded was centre-cropped by the browser, so a photo that was not already square was cropped by luck. Picking a photo now opens a framing step — drag to reposition, scroll or use the slider to zoom, and what sits inside the circle is what is saved.
    Framing on a canvas also drops the orientation data that makes phone photos appear sideways, and normalises every upload to one size and format regardless of what was chosen.
  • Photo actions live on the profile, next to Edit Profile — Clicking the picture opens the actions directly: see the picture, choose a new one, or remove it. The same entries are in the Edit Profile menu, which is where someone looks when they want to change a colleague's photo rather than their own. An employee with no other editing rights sees only the photo entries there; the information sections stay with administrators.
  • Photos open in a viewer with zoom — Opening a picture used to give a flat, fixed-size image, which is not much use for the one thing people open a photo to do. It now opens in a viewer with zoom in, zoom out, a percentage that returns to fit when clicked, drag to pan, scroll and keyboard shortcuts, and Escape to close.
  • A new photo appears everywhere at once — Uploading a photo changed it on the profile page while the sidebar beside it carried on showing initials until the page was reloaded — and for anyone whose photo was set after they signed in, not even a reload helped, only signing out and back in. Each part of the app kept its own private copy of the image and none of them could tell the others that it had changed; the sidebar, separately, read the photo from a snapshot written once at sign-in and never updated. There is now one shared copy, and a photo changed anywhere is picked up in place by everything showing that person.
  • Photos now show where only initials did — Around thirty places drew their own initials circle and could never show a photo at all: the people directory and org chart, approvals, timesheets, schedules, projects and issues, time tracking, the team map, reports and client activity. They now all render the same component.
    Four of those could not show a photo even when asked to, because the data behind them left the photo out; those queries now include it. Two more were worse than empty: the Time and Leave report tabs passed a storage path straight to the browser as an image address, so every person with a photo rendered as a broken image, and the initials fallback never ran because the path looked like a valid value.

Permissions

  • Departments are administrator-only — Creating a department and running organization setup had no role check at all, so any employee could add departments or re-run setup and rewrite the leave year, default allowance and timezone. Both now require an administrator or executive. Reading the department list is unchanged, since everyone needs it for filters.
  • A department head can rename the department they lead — Previously they could not correct even the name of their own team. They can now change its name, description and colour. The code, the active state and the head assignment stay with administrators, because those decide the department's identity and who controls it rather than how it reads.
  • Data import is administrator-only, and hidden from everyone else — Creating an import job already required an administrator, but nothing else in the pipeline did. With a job left part-finished, any employee could run it, roll it back or delete it — and running one writes employee and department records. The row-level error report was readable too, which quotes the uploaded file and therefore colleagues' details. Every import endpoint now requires an administrator or executive. The settings entry was already hidden, but the page itself had no guard, so a typed URL rendered the whole import interface.

Security

  • Values are escaped before they reach an email — Email bodies are assembled as text, and names were being placed into them without escaping. A name containing a link would have rendered as a working link inside a message carrying our branding and sent from our domain, sitting above the genuine button. Every value interpolated into an email is now escaped; ordinary names are unaffected.
  • Invitations sent by an import are held to the plan's user allowance — Inviting turns one upload into one email per row from our own sending domain, so the number that can be sent is bounded rather than left to the size of the file.

Fixes

  • Calendar tooltips are no longer cut off — Hovering a day near the right-hand edge of the dashboard calendar showed a tooltip clipped by the card, so the leave type and status were unreadable exactly where the grid is busiest. Tooltips now stay within the window wherever the day sits, and still point at the day they describe.

v1.0.20

Data import and migration from 48 HR platforms, plus a security and stability pass

This release adds Data Import & Migration, a guided way to bring an existing HR system's people, departments, and time-off history into BookYourPTO. It ships with built-in column mappings for 48 platforms, direct API connections for four of them, and a preview step that writes nothing to the database until it is approved. Alongside it: the framework and its dependencies were moved onto patched releases, and three regressions that came with that upgrade were found and fixed before release.

Data Import & Migration

  • A four-step wizard — Upload a file, confirm how its columns map onto BookYourPTO fields, review a preview of exactly what will be created, then import. The wizard is at Settings → Data Import and is available to administrators and executives.
  • Nothing is written until you approve it — The preview validates the first 100 rows and shows what each one would create, so problems with the mapping surface before anything reaches the database. Rows that cannot be imported are reported with the reason and skipped, so a single malformed date does not fail the whole file.
  • Imports can be rolled back for 72 hours — A completed import can be reversed from its detail page within three days, which matters when a mapping turns out to have been wrong on the second look rather than the first.
  • 48 platforms recognized automatically — Built-in templates cover 63 column layouts across 48 platforms. The uploaded file's headers are matched against them, so in most cases the mapping is already filled in when the step opens. Any column can still be remapped by hand, and unmatched columns are skipped rather than guessed at.
  • Employees, leave history, and departments — Each is imported separately, so a migration can be done in stages: people first, then their historical time off. Leave rows are matched back to employees by email within the organization.
  • CSV and Excel — Both .csv and Excel workbooks (.xlsx, .xls) are accepted, since most HR systems export one or the other and few offer a choice. Files can be up to 25MB and 10,000 rows; larger migrations can be split across several imports.
  • Sample files — A sample CSV for each of the three data types can be downloaded from the import page, for teams building a file by hand rather than exporting one.
  • Save your own column mappings — An organization can save a custom mapping as a reusable template. Custom templates take priority over the built-in ones, which covers systems that export a bespoke report layout.
  • Canadian coverage — 12 of the supported platforms are Canada-first: Collage HR, Employment Hero (formerly Humi), Folks HR, Payworks, Rise People, Wagepoint, Payment Evolution, Avanti Software, Nethris, PurelyHR, Wave Payroll, and Knit People. Folks HR serves Quebec employers and exports in the account language, so its templates recognize both English and French column headers rather than requiring every column to be remapped by hand.

Direct platform connections

  • Import without exporting a file — BambooHR, Timetastic, Employment Hero (formerly Humi), and Zoho People can be connected directly. Once connected, employees and leave history are pulled straight from the account and can be re-imported at any time without producing a new export.
  • BambooHR, Timetastic, and Employment Hero connect with an API key. BambooHR also needs the account subdomain.
  • Zoho People connects by signing in to Zoho, so no key needs to be copied between systems. The connection uses the Canadian Zoho region.
  • Connections are tested before they are saved — Credentials are verified against the provider when the connection is created, so a mistyped key is reported immediately rather than at the first import.

Security

  • Framework and dependency updates — The application framework and its build toolchain were moved onto patched releases, closing every advisory raised by automated security scanning. These covered remote code execution and cross-user data disclosure in server-side rendering, an authentication bypass on mixed-case route paths, a cross-site-scripting issue in HTML sanitization, and a development-tools issue that could allow command execution on a developer's own machine. Automated scanning reports no remaining advisories.
  • The full test suite and a production build were verified on the new versions, and the three regressions the upgrade introduced were found and fixed rather than shipped. They are listed below.

Fixes

  • Authenticated requests could be sent without their credentials — A framework change altered when the shared request helper is bound, with the effect that the authorization header stopped being attached. The application would load and then every request to the API would be rejected. Requests now resolve the helper at call time, and a regression test pins that behavior so a future upgrade fails the build instead of the login.
  • The production server would not start — The build stopped emitting part of a required runtime dependency, so every page request returned an error while the API kept answering normally. That combination would have reported a deployment as healthy. The dependency is now bundled directly, with a test that fails if the incomplete form reappears.
  • Leave records on the edge of a time zone were skipped on import — The API connectors ended their fetch window on the server's UTC day. An organization running ahead of UTC had already moved into a day UTC had not reached, so time off booked on their current day fell outside the window and was never imported, with the job still reporting success. The window now extends past every real time zone offset.
  • Hours could be imported as days — The Payworks leave template mapped an hours column onto the day-count field, which would have recorded 7.5 hours as 7.5 days. The row would have validated and imported cleanly while overstating the employee's usage. The mapping was removed and a check now scans every leave template for the same class of mistake.
  • Platform logos were missing from the templates list — The built-in templates page showed a generic icon for all 63 entries. Logos are now served from a single shared list used by every screen that shows them.
  • Searching the platform picker returned an error — Typing in the platform search field raised an error instead of filtering the list.

v1.0.19

Probation review tracking, accurate leave carry-over, and a stricter browser security policy

This release adds Probation Milestone Tracking — a new employee event that sits alongside birthdays and work anniversaries on the dashboard and calendar, with automated reminder emails so a probation review never quietly slips past its date. It also lands a substantial correctness pass on leave carry-over, where the balance shown on the calendar and the balance the booking form enforced could disagree. Rounding it out: leave allowance becomes a proper administrator control, the browser-side security policy is now enforced rather than advisory, and product analytics is added under the existing consent flow.

Probation Milestone Tracking

  • Probation period on every employee — When creating or editing an employee you can now set a probation period of None, 3 Months, or 6 Months. The Add User form also gained a Hire Date field, so a new starter can be set up with their probation in one pass.
  • The review date calculates itself — The probation review date is derived from the hire date plus the probation period (hired Jan 15 on a 3-month probation → review due Apr 15). Both the employee form and the Add User form show a live preview of the resulting date as you choose, and month-end dates are handled sensibly — a Nov 30 hire on a 3-month probation reviews on Feb 28 (or Feb 29 in a leap year), never spilling into March.
  • Extend probation when you need to — Administrators can override the calculated date with a custom Probation Review Date. When set, it takes precedence everywhere the milestone appears and is labelled as extended, so it is obvious the date was moved by hand rather than calculated.
  • On the dashboard and the calendar — Probation reviews now render as their own employee event, using a dedicated icon and colour distinct from birthdays and work anniversaries, with a tooltip naming the employee, the probation length, and whether it was extended. Managers can spot an upcoming or overdue review at a glance without working the date out by hand.
  • On the employee profile — The Job tab now shows Hire Date, Probation Period, and the calculated (or overridden) Probation Review Date together, giving HR a single reference point when reviewing someone's record.
  • Automated reminder emails — A reminder now goes out 14 days before, 7 days before, and on the probation review date. Each email carries the employee's name, position, department, hire date, probation length, and review date, plus a direct link to their profile so the reviewer can start immediately. A matching in-app notification is raised at the same time.
  • Choose who gets reminded — Reminder recipients are configurable per organization from the notification settings: the employee's direct manager, their department head, all administrators, all executives, or any combination. Reminders can also be switched off organization-wide. Manager and department head are resolved per employee, so each reminder reaches the people who actually run that person's review.
  • Reminders follow the employee's calendar — The countdown to a review is evaluated in the employee's own timezone (falling back to the organization's), so a review due "today" means today where that person works, not where the server happens to run.
  • Probation status stays confidential — Unlike a birthday, whether someone is on probation is only visible to the employee themselves, their direct manager, their department head, and administrators or executives. Other colleagues simply do not see the milestone.

Leave carry-over accuracy

  • The calendar and the booking form now agree — A user with a custom allowance plus a manual carry-over adjustment could see one figure on their calendar balance card and be told a different, smaller number when they tried to book. Every place that computes a leave balance — the balance card, the dashboard people list, the booking check, and the leave edit check — now runs through one shared calculation, so the number you are shown is the number that is enforced.
  • Carry-over no longer repeats every year — A manual carry-over adjustment is now anchored to the fiscal year it was granted for. Previously an adjustment had no year of its own, so every subsequent fiscal year kept counting it — days granted for one year still appeared in the next.
  • No more double-counted carried days — The allowance tile on the balance card was adding carried days on top of a total that already included them, so someone with 15 base days plus 5 carried read "16 / 25 days" instead of "16 / 20". The tile now shows the entitlement before carry-over, so the row reads base + carried − used = remaining.
  • Bookings check the right year — A leave request is now balanced against the fiscal year the leave actually falls in, rather than the year the request happens to be filed in. Booking next January's holiday in December is checked against next year's allowance.
  • Expired adjustments clear themselves — Once a carry-over adjustment's expiry date passes, the days stop counting toward the balance and the figure is now cleared from the profile automatically. Previously it lingered on the record and an administrator had to zero it by hand.
  • A week's warning before days lapse — A new daily reminder notifies the employee's department head — falling back to administrators and executives when they have no manager — seven days before a carry-over adjustment expires, in-app and by email, so someone can extend the expiry or top the days up before they are lost. Day counting runs in the employee's own timezone, and the reminder cannot fire twice for the same adjustment.
  • Clearer allowance wording — The Leave Allowance tab now names the fiscal year an adjustment applies to and states that the days are cleared automatically afterwards, instead of implying they are permanent.

Permissions

  • Leave allowance is an administrator control — The fields that decide how many paid days someone can book — custom allowance, carry-forward settings, and carry-over days — are now restricted to administrators and executives organization-wide, and to department heads for members of their own department. Nobody can change their own.
  • Probation is an administrator control — Probation period and the probation review date override are restricted the same way, so the date that drives the reminder emails cannot be moved by the person being reviewed.

Privacy & Security

  • Stricter browser security policy, now enforced — The application's content security policy moved from advisory to actively enforced, so the browser blocks anything outside the vetted allowlist. Additional cross-origin isolation protections were added at the same time, and the server no longer advertises its underlying framework in responses.
  • Security disclosure contact published — The application now publishes a standard machine-readable security contact so researchers have a clear route to report an issue.
  • Dependency security updates — Third-party dependencies flagged by automated security scanning were updated to patched releases across the tree, with the full test suite and a production build verified on the new versions.
  • Product analytics added under the analytics consent category — A product-analytics tool was added to help us understand how features are actually used. It loads only after a visitor grants analytics consent, only on our own canonical web domains, and never inside the mobile app's web session or on white-label customer domains. Withdrawing analytics consent switches it off immediately.
  • Everyone is asked to consent again — Because a new vendor joined the analytics category, the consent banner reappears once for every visitor, with no pre-ticked toggles and nothing loading until a fresh choice is made. The new vendor is disclosed by name in the cookie Customize panel and the category description was updated to match.

Fixes

  • Icons render across the app again — Icons are now bundled with the application rather than fetched at runtime, restoring them everywhere under the enforced security policy and removing a network round trip on every page.
  • Document and PDF previews render again — Document previews, template detail views, expense receipt viewing, and the personal data-drive preview all display correctly under the enforced policy.
  • Location maps render again — The map pane on the document and timesheet location views displays again, including for organizations without their own maps key configured.

Behind the scenes

  • Probation review dates resolve through a single shared implementation used by the server, the dashboard, the calendar, the profile, and the reminder job — so a date shown in the UI can never disagree with the date a reminder fires on. Leave carry-over math was consolidated the same way, replacing four drifting copies with one.
  • All calendar-day handling in the new code follows the project's date-only convention, with regression tests exercising non-UTC organization timezones on both sides of UTC, month-end clamping, and leap years.
  • Demo data now includes probation periods, extended reviews, and reminder recipients, so the demo environment reflects the new feature.

Full Changelog: https://github.com/anhourtec/BookYourPTO-SaaS/compare/v1.0.18...v1.0.19

v1.0.18

Privacy hardening for leave, safer sign-in, and expense workflow polish

This release closes a set of leave-privacy gaps so a leave marked Private stays private everywhere it is surfaced — the dashboard, the calendar, the iCal feed, digest emails, and the mobile app — and tightens who can act on a cancellation request. It also hardens the sign-in forms and brings a batch of expense-workflow improvements on the web.

Privacy & Security

  • Private leave stays private, end to end — A private leave type no longer leaks its purpose to people who shouldn't see it. Every free-text field on a leave (the reason, notes, approver comments, and rejection / cancellation reasons) is now hidden from anyone who isn't the leave's owner, an admin/executive, or the owner's department head. Previously a colleague in another department could read a private leave's cancellation reason from the details view.
  • Consistent redaction across every surface — The same rule is now applied wherever leaves are shown to others: the dashboard, the per-user calendar, the subscribable iCal calendar feed, and the scheduled digest emails. A department head subscribing to a direct-reports iCal feed no longer receives the real type name or reason of a private leave belonging to someone outside their department.
  • Cancellation review limited to the right approvers — Approve / Decline actions on a leave cancellation are now shown only to the people actually authorized to review that request (the owner's department head, or an admin/executive), on both web and mobile.
  • Safer authentication forms — The login, registration, and SSO sign-in forms now submit explicitly over POST, and the UI library was updated to a version that guarantees the same, so credentials can never be placed in a URL if a form is submitted before the page finishes loading.

Expenses

  • Download all receipts as a single ZIP — Export every receipt on an expense report in one archive instead of saving them one at a time.
  • All Claims filter — The expenses list gains an "All Claims" filter so you can see every claim in one place.
  • Clearer receipt uploads — Upload prompts now list every supported receipt format, and the claim header carries a hover help note explaining the workflow.
  • Verify scanned figures — After a receipt scan, a reminder now prompts you to confirm the extracted amounts before submitting.
  • Cleaner per-diem view — The per-diem Actions column is hidden once a report is no longer editable, removing controls that would not do anything.

v1.0.17

Mobile parity: provided meals, world currency, and full document signing in the app

This release brings the iOS and Android apps up to parity with the web product. The expense, per-diem, and document-signing features that shipped on the web in v1.0.16 are now fully available on mobile, alongside more reliable geofenced clock-in and a fix for off-by-one expense dates.

Mobile

  • Per-diem provided meals — Individual per-diem meals (breakfast, lunch, dinner) can now be marked as provided rather than reimbursed in the app, each with its own source (third party, company card, or a colleague). The provided meal's value is deducted from that day's per-diem, and the per-diem grid shows the provider per meal per day with per-day editing and removal — matching the web bookkeeper view.
  • World-currency support — The app now offers the same full list of world currencies as the web settings (up from a short list), with the correct currency symbol rendered consistently across line items, expense detail, mileage, receipts, and approvals instead of a hard-coded "$".
  • Multi-field document signing — The mobile signing flow now fetches the signer's assigned fields and fills them in: signature and initials are auto-filled with the drawn mark, and the app prompts for any text, date, or checkbox fields before submitting the real field values. Field-based documents that previously failed to sign on mobile now complete correctly.
  • Turn-order signing privacy — In sequential signing, a signer whose turn has not yet arrived no longer sees the fill-and-sign screen. Non-assignees get a read-only monitor view, and a signer who is next in line but not yet up sees a clear "It's not your turn to sign yet" message instead of a raw error.
  • Document audit trail — Administrators, executives, and department heads can now open a document's full audit trail from the document view in the app, mirroring the web History timeline.
  • More reliable geofenced clock-in — The app now sends GPS accuracy with every clock-in, so organizations that require location for clock-in no longer reject a valid punch when accuracy data was missing.

Bug Fixes

  • Off-by-one expense dates — Trip ranges and line-item / mileage dates in the app rendered some calendar-day fields a day early for users west of UTC. They now read the stored UTC day correctly, matching the web fix from v1.0.16.

v1.0.16

Document E-Signatures, a Reworked Time-Tracking Suite, a Redesigned Profile, and a Big Security Pass

This is a large release. The headline is document e-signatures — you can now route a document (or a finalized timesheet) for a dated, audit-trailed signature without leaving the app. Around it, the time-tracking suite was substantially reworked (period timesheets that finalize into a signable PDF, configurable approval routing, a guided setup wizard, multi-day manual entry, and a live Team Status board), the profile page was rebuilt in a tabbed, HR-suite-style layout, documents and onboarding documents got a design refresh, and we closed every open dependency advisory (25 → 0) alongside a round of CSP and SSRF hardening.

New Features

  • Document e-signatures — Documents can be sent for signature with field placement, sequential or parallel multi-signer ordering, per-signer due dates, a SHA-256 content hash, a multi-signer completion certificate, and a full audit trail, all stored encrypted. Signers are notified in-app and by email at each step. The signing UI deliberately avoids ESIGN-Act / "legally binding" language.
  • Period timesheets that finalize into a signable document — Time entries now roll up into a period timesheet (weekly / biweekly / semi-monthly / monthly) with an OPEN → SUBMITTED → APPROVED → FINALIZED → SIGNED lifecycle. When a manager finalizes a period, the system generates a complete, branded timesheet PDF (daily breakdown, regular/overtime/break/leave/holiday totals, pay snapshot) and routes it into /documents for a dated e-signature, reusing the same signing pipeline. Finalizing also advances the payroll lockdown window.
  • Configurable timesheet approval routing — A new Approval routing setting decides who signs a finalized timesheet: No signature (finalize straight to payroll), Single approver (one chosen person signs every sheet), or Department head (routes to the employee's department head, escalating to a chosen approver for heads/admins). Routing is paired with a separate "who signs" order (employee, approver, or employee-then-approver) and a per-signer signing window.
  • Time Clock setup wizard — Admins and executives get a guided, three-step setup on /time-tracking (opened from a "Time clock setup" button): the payroll cycle (week start, cycle, period anchor), location tracking (capture clock-in/out location, or off), and what time is tracked against (nothing, projects, or projects and tasks — wired to the real Projects/Tasks system). It writes straight to the existing settings.
  • Multi-day and break-aware manual time entry — The Add/Edit time-entry panel now supports an unpaid break (subtracted from the worked total) and a Multiple days mode that stamps one time-of-day across a weekday-filtered date range — one entry per employee per selected day, instead of adding days one at a time.
  • Team Status board — A manager-only board (/time-tracking/team) showing who is on shift right now: a live "who's working" list, a full-width GPS map with a marker per clocked-in user (using their real profile photo), clock-in address and elapsed time, plus an "Everyone" roster with today's hours.
  • Redesigned profile page — The profile was rebuilt as a tabbed, HR-suite-style layout (Personal, Job, Time Off, Documents, Performance, and more) for a far richer, more navigable employee record.
  • Onboarding documents — New hires can be assigned documents to read and sign as part of onboarding, tracked to completion.
  • Per-diem provided meals — Individual per-diem meals (breakfast, lunch, dinner) can be marked as provided rather than reimbursed, each with its own source (third party, company card, or a colleague), and the provided meal's value is deducted from that day's per-diem. Providers are managed per-meal in expense settings, and the per-diem grid lets a bookkeeper set the provider per meal per day.
  • Guided workspace setup checklist — New organizations get a dismissible "Finish setting up your workspace" card on the dashboard, visible to administrators and executives only, that tracks four quick settings: timezone & business days, public-holiday location, leave types, and expenses & per-diem. Each step's status is auto-detected from real configuration. Crucially, settings we pre-fill at sign-up (timezone, a starter set of leave types, default expense rates) are surfaced as "pre-filled — review" with a one-click Looks good confirmation rather than silently shown as complete, so the values we guessed actually get verified before they count as done. The progress bar reflects only confirmed steps. The first-run welcome dialog and the product tour are now sequenced so they never appear at the same time — the profile dialog comes first, then the tour on the next dashboard load.

Improvements

  • Documents design refresh — The documents experience was reorganized (Inbox / Sent / Action Required style surfacing) so it's clearer what needs your signature versus what you've sent.
  • Row actions consolidated into an ellipsis menu — Timesheet finalization rows now use a compact ellipsis action menu (Submit / Approve / Reject / Finalize & send / Finalize only / View document) instead of a row of buttons, which also reads cleanly on mobile.
  • Finalize actions follow the auto-send setting — When the org has "Automatically send finalized timesheets for signature" enabled, finalizing is a single Finalize & send action (it always routes for signature). With auto-send off, both Finalize & send and Finalize only are offered so a manager can choose per timesheet, and the two are genuinely distinct — "Finalize only" never sends. With no signature configured, a single Finalize for payroll.
  • Finalized timesheets file into a Timesheets folder — A signed/finalized timesheet PDF now appears in a dedicated Timesheets folder under /documents (it was previously only findable via search), and the folder shows a live count. The category is system-managed, so it isn't offered as a manual upload option.
  • Geofencing available on Pro and above — Plan limits were updated so geofencing is included from Pro upward.
  • Faster, reliable approvals counts — The pending-approvals badge cache is now busted immediately on approve/reject, and /api/approvals/counts is scoped to the caller's approval rights.
  • Referral tracking — Referral attribution now captures the ?via= parameter and persists it via cookie fallback.
  • Per-diem bookkeeper summary — The per-diem display was rebuilt as a per-day summary that reads cleanly for bookkeepers, showing each day's meals, their provided/excluded status, and the amount actually paid.
  • Redesigned travel route entry — The travel and mileage route entry was redesigned to resemble a turn-by-turn directions view, with address autocomplete that falls back to a plain text input when autocomplete isn't available.
  • View Audit Trail from any folder — Administrators, executives, and department heads get a "View Audit Trail" row action on every document folder, opening the document with its History expanded. Previously the trail was only reachable from the Sent view.
  • Compact document audit trail — The History timeline is now a fixed-height scroll area and collapses repeated views by the same person into a single counted row, so a heavily-opened document no longer stretches the detail page.
  • Upload defaults to signing — The document upload entry points open the send-for-signature flow by default; filing to a folder stays available via the mode toggle or a ?mode=store link.
  • Per-recipient field placement — The signing-field placer no longer forces signature/initials parity across signers; it only requires that each recipient has at least one field, and it previews the burned-in "Signed by" label and timestamp around placed signature and initials boxes.
  • Themed sidebar counts — The sidebar count badges (Approvals, Action Required, and others) now use the primary color instead of amber.
  • Currency-neutral expense settings — The expense per-diem and rate fields now offer a full list of world currencies (up from four) and render the selected currency's symbol live across every input and the per-diem section heading. Region-specific guidance copy was removed so the defaults read neutrally for any organization, wherever it operates.

Responsive layout

  • Tables no longer overlap their headers on small screens — Wide data tables (approvals, expenses, timesheets, documents, projects, security logs, and more) compressed their fixed columns on narrow viewports until the header labels overlapped. They now keep a minimum width and scroll horizontally instead, with non-wrapping headers; desktop is unchanged.
  • Create/Edit Shift opens as a side drawer — Scheduling a shift now opens a right-side push drawer (matching the time-entry panel) that sits beside the page and collapses the sidebar, rather than a centered modal over the content.
  • Sidebar no longer flickers on resize — Crossing the desktop/mobile breakpoint used to briefly animate the sidebar into a broken-looking state; layout transitions are now suppressed while the window is actively resizing.

Security

  • All dependency advisories closed (25 → 0) — A sweep of dependency vulnerabilities brought the dependency audit to zero, including switching the PDF rendering library to its legacy build, hardening the PDF worker CSP, and patching an HTTP-client SSRF advisory.
  • CSP and framing hardening — The web analytics script was added to the CSP script-src/connect-src; X-Frame-Options was relaxed to SAMEORIGIN so in-app PDF receipts render; the PDF worker runs under a tightened policy.
  • Tighter notification and approval scoping — Leave-submission notifications are scoped to the submitter's department, and approval counts to the caller's rights, so neither leaks cross-team activity.
  • Safer defaults — "Remember me" now defaults to off at login.
  • Administrators can view security logs — Audit-log, sign-in-log, and leave-transaction views (and their exports) are now open to administrators as well as executives, all strictly org-scoped.
  • Sequential signing visibility — In sequential signing, a later signer whose turn hadn't come could view and list the document (including its fields and stored signature data) before the earlier signer signed. Not-yet-their-turn assignments are now excluded from every document read path; administrators, owners, and uploaders are unaffected.
  • No signing on another user's behalf — Administrators can open any assignment for monitoring, but the signing page presented the full fill-and-sign UI for it. Non-assignees are now redirected to the read-only document view; the sign endpoint already rejected the submission server-side.

Bug Fixes

  • Role changes apply immediately — Promotions, demotions, and deactivations now take effect on the user's next request instead of waiting up to the access-token lifetime, and a role change no longer forces a disruptive re-login.
  • Timesheet PDF period dates — The period header on the generated timesheet renders the calendar boundaries in UTC so a "May 18 – May 31" period never shifts a day for a reader in another timezone.
  • Timesheet signature/date alignment — On the generated timesheet, the signature image and date were landing below the Signature/Date lines because the signing fields were placed at a fixed position while the lines move with the number of entries. The fields are now positioned to the actual drawn lines (and on the correct page for multi-page timesheets). Applies to timesheets finalized after this release.
  • Schedule week-clear fix — Clearing a published week referenced a non-existent model and threw at runtime; it now uses the correct schedule-publication record with timezone-aware week matching.
  • Training auto-assignment timing — Corrected a comparison in the training auto-assign scheduler that mis-evaluated due windows.
  • Off-by-one expense dates — The expense detail and overview views rendered some calendar-day fields a day early in timezones west of UTC; they now read the stored UTC day correctly.
  • Initials-only signers couldn't sign — A signer asked only to initial (with no signature field) submitted an empty signature and was rejected with a 400; the signing flow now uses the initials image as the signature.
  • Right-panel space released on navigation — Leaving a page with an open right-side push drawer (for example a schedule or timesheet entry) without closing it left the reserved content-shell width behind; it is now released when the page unmounts.

v1.0.15

Leave Cancellation & Editing, a Redesigned Expenses Overview, and Smarter Dashboards

This release rounds out the leave workflow: employees can finally edit or cancel a pending request, and request cancellation of an already-approved leave with manager sign-off, all from the dashboard or calendar — no more emailing HR when plans change. Alongside it, the expenses list gets a redesigned overview with a status funnel and an interactive spend chart, the dashboard learns to surface the people you actually work with, approvers get a live count of what's waiting on them, and we close two dependency security advisories. There's also a new consent-gated cookie banner and proactive white-label domain-expiry reminders.

New Features

  • Cancel or edit a pending leave request — From the leave detail view on the dashboard or calendar, the person who filed a still-pending request can now Edit it (re-opens the booking form pre-filled, then re-validates dates, balance, notice period, and overlaps) or Cancel it outright. Editing notifies the approvers with a distinct "Leave Request Updated" message rather than looking like a brand-new submission.
  • Request cancellation of an approved leave — Plans change after a leave is approved. Instead of an off-system email, the employee can now submit a cancellation request on an approved leave; it goes to the admins / executives / department heads who can Approve it (the leave is cancelled and the balance freed) or Reject it (the leave stays approved). Everyone is notified in-app and by email at each step. Users who already have direct-cancel power (admins/execs) just cancel outright — they don't see the redundant "Request cancellation" button.
  • Pending-cancellation indicator on the calendar & dashboard — An approved leave that has a cancellation request waiting now carries a distinct marker (a small primary-coloured dot, separate from the yellow "pending approval" dot) and an "Approved · Cancellation requested" tooltip, so the state is visible at a glance on both the year calendar and the dashboard timeline.
  • Pending-approvals count on the sidebar — The Approvals sidebar entry now shows a live badge totalling everything waiting on you across leave, expenses, and time — an amber pill when the sidebar is expanded, a dot when collapsed, "99+" past ninety-nine, hidden at zero. Unlike a notification it's a persistent "still to do" signal that doesn't clear when you dismiss notifications, and it only appears for users who can actually approve. Each queue is scoped to the viewer's approval rights, so the badge never counts work you can't action.
  • Expenses overview: status funnel + spend chart — /expenses gains a redesigned header card: a reimbursed-amount hero with active / paid context, a clickable status donut with a legend for filtering, and a spend-over-time chart you can switch between Spend and Claims, Monthly and Daily, and Bar / Line / Area. It's primary-themed, fully responsive, and animates on load. The list's counters were reconciled so "All" matches the toolbar's active total and the info bar reflects the true server-side filtered count.
  • "Sort by relevance" on the dashboard — A smarter default ordering for the user timeline: it puts you first, then blends how often you open and search for each person (a private, in-browser "frecency" signal that favours frequent and recent contacts) with each user's leave-request volume. Your filter and sort choices now also persist across refreshes and sessions. The personalization signal never leaves your browser.
  • Approver context: the requester's timezone & local time — The leave approval slide-over now shows the employee's timezone and their current local time (e.g. "3:26 PM · America/Toronto", or the org default when they haven't set a personal one), so an approver in another zone has the context before they decide.
  • Proactive white-label domain-expiry reminders — For customers on white-label custom domains, the app now looks up the domain's registrar expiry date (via RDAP) and reminds the org's admins at T-30 / T-14 / T-7 / T-1 days before it lapses — in-app and by email — so a branded URL never silently goes dark because a renewal was missed. Privacy-redacted TLDs (common in the EU) fall back to the existing reactive DNS-failure path.
  • Consent-gated cookie banner — A new cookie-consent banner loads analytics and affiliate tracking only after the visitor consents. EU visitors (detected by timezone) must choose explicitly; others get an auto-accept countdown that an explicit choice cancels immediately. Trackers load only on the canonical site — white-label custom domains get no banner and no third-party scripts. The banner is themed off the app's design tokens so it adapts to white-label brand colours.
  • Dashboard user search & pagination — A search-by-name/email box (inline in the header row alongside the filter, count, and period controls) plus a rows-per-page footer, so large teams are easy to find and the desktop timeline stays manageable; mobile still shows everyone.
  • Obsolete-browser upgrade nudge — A tiny standalone script (loaded as a classic, non-module script so it runs even on browsers too old to parse the app) feature-detects modern capabilities and, when they're missing, shows a self-contained "please upgrade your browser" banner. Dismissal is remembered.

Improvements

  • Calendar honours the org's week-start-day — The year calendar grid was hardcoded to a Sunday start regardless of the organisation's setting. A Monday-start org now sees Monday-first columns; the setting threads through the month/year builders and grid headers.
  • Tidier calendar day cells — Day content is now vertically centred so numbers sit on a consistent grid (the previous top-hugging layout made row spacing look uneven), and the cell no longer clips the top of leave pills or the pending-status dot.
  • Cookie banner clears the sidebar on desktop — When signed in, the bottom-left banner now shifts past the fixed sidebar (expanded or collapsed) instead of overlapping it, and stays in the corner on public / logged-out views.
  • Removed a dead "My favourites" filter — The dashboard's account-type filter offered a "My favourites" option that had no backing feature; it's been removed.

Accessibility

  • Calendar day cells are now screen-reader friendly — Event days render an icon with no visible number, so assistive tech previously announced only "button". Both the year-calendar cell and the dashboard timeline cell now carry a descriptive label (the date plus any holiday / leave / birthday summary).

Bug Fixes

  • Approval dates no longer shift a day for approvers in another timezone — Leave start/end are calendar-day values stored at UTC midnight, but the approvals slide-over and the CSV/PDF export were formatting them in the approver's local zone — so a June 3 leave showed as June 2 for anyone west of UTC. Those are now read in UTC (real timestamps like "submitted" stay local). The same fix was applied to the date strings in pending-edit validation messages.
  • Editing a pending request reads as an update, not a new request — Re-submitting an edited leave now sends a "Leave Request Updated" notification (in-app and email) instead of the original "has requested …" copy, with timezone-safe dates.
  • Cancellation-request notification now opens the right place — The "Cancellation Request Pending" notification deep-links to the dashboard (where an approver opens the leave to act on it) instead of doing nothing.
  • Expenses overview survives filtering — The overview card stays mounted across refetches, so clicking a status segment updates it in place instead of remounting and resetting the chart's metric / granularity toggles.

v1.0.14

This release tightens up two areas that have been quietly accumulating rough edges: how billing limits are explained to admins, and how the app handles users whose personal timezone differs from their organisation's. It also adds plan-aware gating around the Document Templates feature so Free-plan customers see a clear upgrade path instead of empty screens, and closes a handful of quota loopholes that could let a single request push an org past its plan limit.

New Features

  • Per-user timezone, end-to-end across booking modals — Following on from v1.0.13's per-user timezone groundwork (searchable picker, profile field, calendar grid highlighting), every booking and date-picker modal now resolves "today" against the viewer's resolved timezone rather than UTC or the browser clock. A Vancouver-based employee in a Toronto-based org now sees their own calendar day pre-filled in Book Time Off, Group Booking, Lock Dates, the expense Add Meal / Add Travel cards, the document upload / assign / template send / personal upload modals, and the onboarding welcome DOB picker. The same rule used for calendar-grid highlighting is now applied everywhere date pickers default to "today".
  • Document Templates usage card on /billing — The Plan Usage panel gains a dedicated "Document Templates" row that shows your current template count against your plan cap (Free 0, Pro 5, Business 50, Enterprise unlimited). On Free plan the card shows an inline "Not available on the Free plan — Upgrade to use templates" prompt instead of an unhelpful 0 / 0 progress bar.
  • Per-counter reset timer on /billing — Each monthly metric (Documents, Receipts Scanning, Projects, Expense Claims) now displays when it next resets — "Resets in 7 days" up close, "Resets Jun 22" further out. The label is computed in the org's timezone and renders only on plans where the counter actually resets (Free is lifetime for most metrics).
  • Subscription-anniversary monthly resets — Counters now roll over on the date Stripe charges your card next, not on the 1st of the calendar month. An org that subscribed on the 22nd resets on the 22nd. Previously a customer who subscribed mid-month would get their counter reset on the 1st (sometimes only days after they paid), which was both confusing and inconsistent with how their invoices arrive.
  • Plan-aware Document Templates landing pages — /documents/templates and /documents/templates/create now render a clean upgrade card for Free-plan organisations explaining what Templates do and what unlocks them, instead of an empty list / disabled wizard. The data fetch is skipped on Free plan so there's no loading flicker before the upgrade card appears.
  • "Pro" lock badges on Template and Bulk Send entry points — The Templates and Bulk Send buttons on the documents toolbar, the "Use a Template" / "Bulk Send" items in the Start menu, and the Document Templates jump button on the onboarding / offboarding settings header now wear a small "Pro" lock pill on Free plan. Tooltips explain the dependency (e.g. "Pro plan required — depends on Document Templates" on Bulk Send).
  • Bulk Send page gated on Free — /documents/bulk-send now renders an upgrade card explaining that Bulk Send needs Document Templates, instead of letting Free users build a bulk-send wizard they can't submit.
  • Add Task modal explains the upgrade requirement inline — The "Attach Document Template" picker inside the onboarding / offboarding Add Task modal now swaps its dropdown for an inline upgrade prompt when the org is on Free, so admins know why the picker is empty and can still save the task without a template.

Improvements

  • State-aware trial-ending banner — The "Trial ending soon" banner previously fired a single "Upgrade Now" call-to-action for every trialing org, even when the trial was set to auto-convert and the customer had a card on file. The banner now picks one of two paths: a soft, no-CTA informational banner for trials that will auto-convert ("Your Pro subscription will start automatically on May 29. Cancel anytime."), and an actionable warning with a Reactivate CTA only for trials the customer has actively cancelled. Matches the pattern Stripe, Linear, and Notion all use.
  • /billing usage display now lines up with the actual server gate — Several monthly counters were computing "this month" using the server's local timezone (UTC in production), while the receipts counter already used the org's timezone. Around month boundaries the billing UI could show a different counter from what the gate enforced at submit time. Documents, Projects, Expense Claims, and the usage display all now bucket by the same boundary the gate uses, so the page and the API agree.
  • TypeScript hygiene in the Leave Request modal — Replaced a handful of string.split(':').map(Number) destructures with a shared hmToMinutes helper, resolving the "possibly undefined" complaints TypeScript was raising on every time-slot arithmetic. The /api/leaves/schedule client fetch was also switched from params to query to match how the Nitro handler reads its arguments and to resolve a type-instantiation-depth error.

Bug Fixes

  • Booking modal no longer prefills tomorrow's date in the evening — In any timezone west of UTC, opening the Book Time Off floating action button after roughly 5 PM local time used to pre-fill the date pickers with the next day's date (the UTC day had already rolled over even though the user's calendar still showed today). All booking, expense, document, and onboarding date pickers now resolve "today" in the user's resolved timezone, so the picker always matches the day the user sees on their wall clock.
  • Documents Templates feature respects the documented Pro / Business cap — The endpoint for creating reusable templates previously ran only a role check and never consulted the per-plan quota. A Pro org could create more than the documented 5 active templates. The endpoint now enforces the cap (Free 0, Pro 5, Business 50, Enterprise unlimited) and the billing page surfaces the live count.
  • Template send and direct upload can no longer overshoot the document quota — A single template send or document upload that creates multiple Document and DocumentAssignment rows is now pre-checked against the projected delta (1 file + N signers) rather than just the current count. An org at 49 / 50 documents sending a template to 10 signers used to push the count to 60; that request is now rejected up front with a message explaining how many units would be consumed.
  • Receipt-scan counter is now race-free — Two simultaneous receipt scans submitted at the per-month boundary used to both pass the limit check before either incremented, allowing one extra AI call past the cap. The counter is now reserved atomically through a single conditional update; the second concurrent request is rejected before the AI provider is called, and a failed AI call refunds the reserved slot so the user isn't billed against their quota for a scan that couldn't be read.
  • Dashboard "Book time off" picker honours the per-user timezone override — Earlier in the release the calendar grid started highlighting "today" in the viewer's per-user timezone, but the dashboard's Book Time Off modal was still pulling the user's timezone from a JWT-and-localStorage cache that isn't refreshed after a profile edit. The modal now sources the timezone from the same bootstrap data the grid uses, so a user who changes their personal timezone sees the change reflected everywhere immediately.
  • Group Booking and Lock Dates modals are now timezone-aware — Both modals defaulted "today" using UTC, which mismatched the highlighted day on the calendar in any negative-offset timezone late in the day. They now use the same per-user-then-org resolution rule as Book Time Off.
  • Add expense (Meal / Travel) date pickers no longer pre-fill UTC today — Same class of bug, same fix: meal and travel expense cards prefill the date in the viewer's resolved timezone instead of UTC.
  • Document upload, assign, and template send minDate honours the org timezone — The minimum-due-date constraints on document upload modals are now computed in the org's resolved timezone so users in negative-offset zones can still pick "today" late in the evening.
  • Onboarding DOB picker max bound is now in the viewer's timezone — Same fix applied to the onboarding welcome modal's max attribute on the date-of-birth input.