[{"data":1,"prerenderedAt":396},["ShallowReactive",2],{"$f1ggjwtm5e8hdo":3,"mdc--hqyztg-key":15},{"release":4,"newer":10,"older":12,"page":14},{"tag":5,"name":5,"publishedAt":6,"url":7,"prerelease":8,"markdown":9},"v1.0.15","2026-06-02T20:56:49Z","https:\u002F\u002Fgithub.com\u002Fanhourtec\u002FBookYourPTO-SaaS\u002Freleases\u002Ftag\u002Fv1.0.15",false,"### Leave Cancellation & Editing, a Redesigned Expenses Overview, and Smarter Dashboards\r\n\r\nThis 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.\r\n\r\n### New Features\r\n\r\n- **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.\r\n\r\n- **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 \u002F executives \u002F 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\u002Fexecs) just cancel outright — they don't see the redundant \"Request cancellation\" button.\r\n\r\n- **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.\r\n\r\n- **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.\r\n\r\n- **Expenses overview: status funnel + spend chart** — `\u002Fexpenses` gains a redesigned header card: a reimbursed-amount hero with active \u002F 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 \u002F Line \u002F 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.\r\n\r\n- **\"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.\r\n\r\n- **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\u002FToronto\", or the org default when they haven't set a personal one), so an approver in another zone has the context before they decide.\r\n\r\n- **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 \u002F T-14 \u002F T-7 \u002F 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.\r\n\r\n- **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.\r\n\r\n- **Dashboard user search & pagination** — A search-by-name\u002Femail 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.\r\n\r\n- **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.\r\n\r\n### Improvements\r\n\r\n- **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\u002Fyear builders and grid headers.\r\n\r\n- **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.\r\n\r\n- **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 \u002F logged-out views.\r\n\r\n- **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.\r\n\r\n### Accessibility\r\n\r\n- **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 \u002F leave \u002F birthday summary).\r\n\r\n### Bug Fixes\r\n\r\n- **Approval dates no longer shift a day for approvers in another timezone** — Leave start\u002Fend are calendar-day values stored at UTC midnight, but the approvals slide-over and the CSV\u002FPDF 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.\r\n\r\n- **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.\r\n\r\n- **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.\r\n\r\n- **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 \u002F granularity toggles.",{"tag":11,"name":11},"v1.0.16",{"tag":13,"name":13},"v1.0.14",1,{"data":16,"body":17},{},{"type":18,"children":19},"root",[20,29,50,56,279,285,328,334,347,353],{"type":21,"tag":22,"props":23,"children":25},"element","h3",{"id":24},"leave-cancellation-editing-a-redesigned-expenses-overview-and-smarter-dashboards",[26],{"type":27,"value":28},"text","Leave Cancellation & Editing, a Redesigned Expenses Overview, and Smarter Dashboards",{"type":21,"tag":30,"props":31,"children":32},"p",{},[33,35,41,43,48],{"type":27,"value":34},"This release rounds out the leave workflow: employees can finally ",{"type":21,"tag":36,"props":37,"children":38},"strong",{},[39],{"type":27,"value":40},"edit or cancel a pending request",{"type":27,"value":42},", and ",{"type":21,"tag":36,"props":44,"children":45},{},[46],{"type":27,"value":47},"request cancellation of an already-approved leave",{"type":27,"value":49}," 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.",{"type":21,"tag":22,"props":51,"children":53},{"id":52},"new-features",[54],{"type":27,"value":55},"New Features",{"type":21,"tag":57,"props":58,"children":59},"ul",{},[60,92,123,133,150,183,215,225,242,259,269],{"type":21,"tag":61,"props":62,"children":63},"li",{},[64,69,71,76,78,83,85,90],{"type":21,"tag":36,"props":65,"children":66},{},[67],{"type":27,"value":68},"Cancel or edit a pending leave request",{"type":27,"value":70}," — From the leave detail view on the dashboard or calendar, the person who filed a still-",{"type":21,"tag":36,"props":72,"children":73},{},[74],{"type":27,"value":75},"pending",{"type":27,"value":77}," request can now ",{"type":21,"tag":36,"props":79,"children":80},{},[81],{"type":27,"value":82},"Edit",{"type":27,"value":84}," it (re-opens the booking form pre-filled, then re-validates dates, balance, notice period, and overlaps) or ",{"type":21,"tag":36,"props":86,"children":87},{},[88],{"type":27,"value":89},"Cancel",{"type":27,"value":91}," it outright. Editing notifies the approvers with a distinct \"Leave Request Updated\" message rather than looking like a brand-new submission.",{"type":21,"tag":61,"props":93,"children":94},{},[95,100,102,107,109,114,116,121],{"type":21,"tag":36,"props":96,"children":97},{},[98],{"type":27,"value":99},"Request cancellation of an approved leave",{"type":27,"value":101}," — Plans change after a leave is approved. Instead of an off-system email, the employee can now submit a ",{"type":21,"tag":36,"props":103,"children":104},{},[105],{"type":27,"value":106},"cancellation request",{"type":27,"value":108}," on an approved leave; it goes to the admins \u002F executives \u002F department heads who can ",{"type":21,"tag":36,"props":110,"children":111},{},[112],{"type":27,"value":113},"Approve",{"type":27,"value":115}," it (the leave is cancelled and the balance freed) or ",{"type":21,"tag":36,"props":117,"children":118},{},[119],{"type":27,"value":120},"Reject",{"type":27,"value":122}," it (the leave stays approved). Everyone is notified in-app and by email at each step. Users who already have direct-cancel power (admins\u002Fexecs) just cancel outright — they don't see the redundant \"Request cancellation\" button.",{"type":21,"tag":61,"props":124,"children":125},{},[126,131],{"type":21,"tag":36,"props":127,"children":128},{},[129],{"type":27,"value":130},"Pending-cancellation indicator on the calendar & dashboard",{"type":27,"value":132}," — 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.",{"type":21,"tag":61,"props":134,"children":135},{},[136,141,143,148],{"type":21,"tag":36,"props":137,"children":138},{},[139],{"type":27,"value":140},"Pending-approvals count on the sidebar",{"type":27,"value":142}," — The ",{"type":21,"tag":36,"props":144,"children":145},{},[146],{"type":27,"value":147},"Approvals",{"type":27,"value":149}," 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.",{"type":21,"tag":61,"props":151,"children":152},{},[153,158,160,167,169,174,176,181],{"type":21,"tag":36,"props":154,"children":155},{},[156],{"type":27,"value":157},"Expenses overview: status funnel + spend chart",{"type":27,"value":159}," — ",{"type":21,"tag":161,"props":162,"children":164},"code",{"className":163},[],[165],{"type":27,"value":166},"\u002Fexpenses",{"type":27,"value":168}," gains a redesigned header card: a reimbursed-amount hero with active \u002F paid context, a clickable ",{"type":21,"tag":36,"props":170,"children":171},{},[172],{"type":27,"value":173},"status donut",{"type":27,"value":175}," with a legend for filtering, and a ",{"type":21,"tag":36,"props":177,"children":178},{},[179],{"type":27,"value":180},"spend-over-time chart",{"type":27,"value":182}," you can switch between Spend and Claims, Monthly and Daily, and Bar \u002F Line \u002F 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.",{"type":21,"tag":61,"props":184,"children":185},{},[186,191,193,198,200,206,208,213],{"type":21,"tag":36,"props":187,"children":188},{},[189],{"type":27,"value":190},"\"Sort by relevance\" on the dashboard",{"type":27,"value":192}," — A smarter default ordering for the user timeline: it puts ",{"type":21,"tag":36,"props":194,"children":195},{},[196],{"type":27,"value":197},"you",{"type":27,"value":199}," first, then blends how often you open and search for each person (a private, in-browser \"frecency\" signal that favours frequent ",{"type":21,"tag":201,"props":202,"children":203},"em",{},[204],{"type":27,"value":205},"and",{"type":27,"value":207}," recent contacts) with each user's leave-request volume. Your filter and sort choices now also ",{"type":21,"tag":36,"props":209,"children":210},{},[211],{"type":27,"value":212},"persist across refreshes and sessions",{"type":27,"value":214},". The personalization signal never leaves your browser.",{"type":21,"tag":61,"props":216,"children":217},{},[218,223],{"type":21,"tag":36,"props":219,"children":220},{},[221],{"type":27,"value":222},"Approver context: the requester's timezone & local time",{"type":27,"value":224}," — The leave approval slide-over now shows the employee's timezone and their current local time (e.g. \"3:26 PM · America\u002FToronto\", or the org default when they haven't set a personal one), so an approver in another zone has the context before they decide.",{"type":21,"tag":61,"props":226,"children":227},{},[228,233,235,240],{"type":21,"tag":36,"props":229,"children":230},{},[231],{"type":27,"value":232},"Proactive white-label domain-expiry reminders",{"type":27,"value":234}," — 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 ",{"type":21,"tag":36,"props":236,"children":237},{},[238],{"type":27,"value":239},"T-30 \u002F T-14 \u002F T-7 \u002F T-1 days",{"type":27,"value":241}," 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.",{"type":21,"tag":61,"props":243,"children":244},{},[245,250,252,257],{"type":21,"tag":36,"props":246,"children":247},{},[248],{"type":27,"value":249},"Consent-gated cookie banner",{"type":27,"value":251}," — A new cookie-consent banner loads analytics and affiliate tracking ",{"type":21,"tag":36,"props":253,"children":254},{},[255],{"type":27,"value":256},"only after the visitor consents",{"type":27,"value":258},". 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.",{"type":21,"tag":61,"props":260,"children":261},{},[262,267],{"type":21,"tag":36,"props":263,"children":264},{},[265],{"type":27,"value":266},"Dashboard user search & pagination",{"type":27,"value":268}," — A search-by-name\u002Femail 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.",{"type":21,"tag":61,"props":270,"children":271},{},[272,277],{"type":21,"tag":36,"props":273,"children":274},{},[275],{"type":27,"value":276},"Obsolete-browser upgrade nudge",{"type":27,"value":278}," — 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.",{"type":21,"tag":22,"props":280,"children":282},{"id":281},"improvements",[283],{"type":27,"value":284},"Improvements",{"type":21,"tag":57,"props":286,"children":287},{},[288,298,308,318],{"type":21,"tag":61,"props":289,"children":290},{},[291,296],{"type":21,"tag":36,"props":292,"children":293},{},[294],{"type":27,"value":295},"Calendar honours the org's week-start-day",{"type":27,"value":297}," — 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\u002Fyear builders and grid headers.",{"type":21,"tag":61,"props":299,"children":300},{},[301,306],{"type":21,"tag":36,"props":302,"children":303},{},[304],{"type":27,"value":305},"Tidier calendar day cells",{"type":27,"value":307}," — 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.",{"type":21,"tag":61,"props":309,"children":310},{},[311,316],{"type":21,"tag":36,"props":312,"children":313},{},[314],{"type":27,"value":315},"Cookie banner clears the sidebar on desktop",{"type":27,"value":317}," — 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 \u002F logged-out views.",{"type":21,"tag":61,"props":319,"children":320},{},[321,326],{"type":21,"tag":36,"props":322,"children":323},{},[324],{"type":27,"value":325},"Removed a dead \"My favourites\" filter",{"type":27,"value":327}," — The dashboard's account-type filter offered a \"My favourites\" option that had no backing feature; it's been removed.",{"type":21,"tag":22,"props":329,"children":331},{"id":330},"accessibility",[332],{"type":27,"value":333},"Accessibility",{"type":21,"tag":57,"props":335,"children":336},{},[337],{"type":21,"tag":61,"props":338,"children":339},{},[340,345],{"type":21,"tag":36,"props":341,"children":342},{},[343],{"type":27,"value":344},"Calendar day cells are now screen-reader friendly",{"type":27,"value":346}," — 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 \u002F leave \u002F birthday summary).",{"type":21,"tag":22,"props":348,"children":350},{"id":349},"bug-fixes",[351],{"type":27,"value":352},"Bug Fixes",{"type":21,"tag":57,"props":354,"children":355},{},[356,366,376,386],{"type":21,"tag":61,"props":357,"children":358},{},[359,364],{"type":21,"tag":36,"props":360,"children":361},{},[362],{"type":27,"value":363},"Approval dates no longer shift a day for approvers in another timezone",{"type":27,"value":365}," — Leave start\u002Fend are calendar-day values stored at UTC midnight, but the approvals slide-over and the CSV\u002FPDF 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.",{"type":21,"tag":61,"props":367,"children":368},{},[369,374],{"type":21,"tag":36,"props":370,"children":371},{},[372],{"type":27,"value":373},"Editing a pending request reads as an update, not a new request",{"type":27,"value":375}," — 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.",{"type":21,"tag":61,"props":377,"children":378},{},[379,384],{"type":21,"tag":36,"props":380,"children":381},{},[382],{"type":27,"value":383},"Cancellation-request notification now opens the right place",{"type":27,"value":385}," — The \"Cancellation Request Pending\" notification deep-links to the dashboard (where an approver opens the leave to act on it) instead of doing nothing.",{"type":21,"tag":61,"props":387,"children":388},{},[389,394],{"type":21,"tag":36,"props":390,"children":391},{},[392],{"type":27,"value":393},"Expenses overview survives filtering",{"type":27,"value":395}," — 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 \u002F granularity toggles.",1790889963334]