Skip to content

Instantly share code, notes, and snippets.

@felipecustodio
Created August 1, 2024 10:12
Show Gist options
  • Save felipecustodio/e3beb63efe03138eed0516b2fe63fe5d to your computer and use it in GitHub Desktop.
Save felipecustodio/e3beb63efe03138eed0516b2fe63fe5d to your computer and use it in GitHub Desktop.
Reset assigned amount for all previous months on YNAB, for a fresh start keeping the transactions categorized
// ==UserScript==
// @name YNAB - Reset Assigned Amounts on All Months
// @version 1.0.0
// @description Reset the assigned amounts on all months - Click the RAW button to install with Tampermonkey
// @author felipecustodio
// @match https://app.ynab.com/*
// @grant none
// @run-at document-end
// ==/UserScript==
// Easier to update selectors here, if page changes
const assignSelector = ".to-be-budgeted-auto-assign.to-be-budgeted-button";
const confirmSelector = ".ynab-button.primary";
const resetSelector = "button[title='Reset all assigned amounts for this month, in case you want a do-over.']";
const previousMonthSelector = ".budget-header-calendar-prev";
const lastMonthSelector = ".budget-header-calendar-prev.disabled";
const headerSelector = ".budget-header-item.budget-header-days.tk-days-of-buffering";
/**
* Wait for a specific selector to be present in the DOM.
* @param {string} selector - The selector to wait for.
* @param {number} timeout - The time to wait before rejecting (default is 300 ms).
* @returns {Promise<Element>} - A promise that resolves with the element or rejects with an error.
*/
function waitForSelector(selector, timeout = 300) {
return new Promise((resolve, reject) => {
const interval = 10;
let elapsedTime = 0;
const check = setInterval(() => {
const element = document.querySelector(selector);
if (element) {
clearInterval(check);
resolve(element);
} else if (elapsedTime >= timeout) {
clearInterval(check);
reject(new Error(`Timeout waiting for selector: ${selector}`));
}
elapsedTime += interval;
}, interval);
});
}
/**
* Click a button identified by a selector.
* @param {string} selector - The selector for the button to click.
*/
async function clickButton(selector) {
try {
const button = await waitForSelector(selector);
button.click();
console.log(`${selector} button clicked successfully`);
// Adding a short delay to ensure the UI updates
await new Promise(resolve => setTimeout(resolve, 300));
} catch (error) {
console.warn(error.message);
}
}
/**
* Reset assigned amount for the current month.
*/
async function resetAssignedAmount() {
await clickButton(assignSelector);
await clickButton(confirmSelector);
await clickButton(resetSelector);
await clickButton(confirmSelector);
}
/**
* Click the button to go to the previous month.
*/
async function clickPreviousMonth() {
await clickButton(previousMonthSelector);
}
/**
* Check if the current month is the last month.
* @returns {boolean} - True if it's the last month, false otherwise.
*/
function isLastMonth() {
return !!document.querySelector(lastMonthSelector);
}
/**
* Reset assigned amounts for all previous months, starting at the current open page.
*/
async function resetAllMonths() {
console.log("Resetting all months");
while (!isLastMonth()) {
await resetAssignedAmount();
await clickPreviousMonth();
}
}
/**
* Add a button to the UI for resetting all assigned amounts, in the header, where Age of Money is displayed.
*/
function addResetAllButton() {
const targetDiv = document.querySelector(headerSelector);
if (targetDiv) {
const newButton = document.createElement("button");
newButton.className = "ynab-button secondary";
newButton.title = "Reset all assigned amounts for all months";
newButton.type = "button";
newButton.innerHTML = `
<div>Reset All Assigned Amounts</div>
`;
targetDiv.appendChild(newButton);
newButton.addEventListener("click", async () => {
console.log("Reset all assigned amounts button clicked");
newButton.innerHTML = `
<div>Resetting...</div>
`;
await resetAllMonths();
});
} else {
console.warn("Target div not found");
}
}
// Immediately invoked function to start the script
(async function() {
console.log("Script started");
// Wait for the page to load fully
await waitForSelector(".budget-header-item.budget-header-days.tk-days-of-buffering", timeout=5000);
addResetAllButton();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment