Created
August 1, 2024 10:12
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==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