An expense tracker
The problem
Build a page where a person can record spending, see totals for each category, delete mistakes, and find everything still there when they come back.
Requirements
- A form adds an expense with a description, an amount and a category, and refuses bad input with a clear message
- The expenses are listed with the newest first, and each can be deleted
- Totals overall and for each category are worked out from the list
- A control filters the list by category
- The data is kept in localStorage, and damaged data cannot break the page
- A button loads sample data through a function shaped like fetch, showing loading and error states
Technologies
- HTML
- CSS
- JavaScript
- The DOM
- Array methods
- Promises
- JSON
- localStorage
Step by step
- Decide on the shape of one expense object, including an id.
- Write functions that load and save the list, with a try block and a shape check.
- Write the HTML: the form, the filter, the totals and an empty list.
- Write one render function that draws everything from the list and the filter.
- Handle the form's submit event, with checking and a helpful error message.
- Use one click listener on the list to handle deleting.
- Calculate the totals with reduce, keeping the amounts as whole numbers of the smallest unit.
- Add the sample data button, with loading, success and failure states.
- Reload the page to test saving, then damage the saved text on purpose and reload again.
Expected result
A small but real application whose data survives a reload, which never shows a wrong total, and which copes with bad input and bad stored data.
How it is evaluated
- Totals always match the list, including after a delete
- Bad input is refused with a clear message
- Damaged stored data does not break the page
- User text is never inserted as HTML
- The code is split into small functions, with one render function
Ways to improve it
- Show spending per category as a simple chart
- Add a monthly budget, with a warning near the limit
- Export the list as a CSV file
- Replace the stand-in with a real API of your own