├── src ├── App.css ├── index.css ├── main.jsx ├── components │ ├── TotalExpenses.jsx │ ├── ExpenseList.jsx │ └── ExpenseForm.jsx ├── App.jsx └── assets │ └── react.svg ├── postcss.config.js ├── vite.config.js ├── tailwind.config.js ├── .gitignore ├── index.html ├── README.md ├── package.json ├── eslint.config.js └── public └── vite.svg /src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ['./index.html', './src/**/*.{js,jsx,ts,tsx}'], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import { StrictMode } from 'react' 2 | import { createRoot } from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/components/TotalExpenses.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const TotalExpenses = ({ expenses }) => { 4 | const total = expenses.reduce((sum, expense) => sum + parseFloat(expense.amount), 0).toFixed(2); 5 | return

Total Expenses: ${total}

; 6 | }; 7 | 8 | export default TotalExpenses; 9 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Expense Tracker App

2 |

Expense Tracker is a simple expense tracker app built in React.js and Tailwind CSS.

3 | 4 |

Installation Instructions

5 |
    6 |
  1. Clone this repository.
  2. 7 |
  3. Open it in your development environment.
  4. 8 |
  5. Run npm install to install the node modules.
  6. 9 |
  7. Finally, run npm run dev to run it in your browser.
  8. 10 |
11 | 12 | -------------------------------------------------------------------------------- /src/components/ExpenseList.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const ExpenseList = ({ expenses, filterCategory }) => { 4 | const filteredExpenses = filterCategory ? expenses.filter(exp => exp.category === filterCategory) : expenses; 5 | 6 | return ( 7 | 17 | ); 18 | }; 19 | 20 | export default ExpenseList; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "expense-tracker", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "autoprefixer": "^10.4.20", 14 | "postcss": "^8.4.47", 15 | "react": "^18.3.1", 16 | "react-dom": "^18.3.1", 17 | "tailwindcss": "^3.4.14" 18 | }, 19 | "devDependencies": { 20 | "@eslint/js": "^9.11.1", 21 | "@types/react": "^18.3.10", 22 | "@types/react-dom": "^18.3.0", 23 | "@vitejs/plugin-react": "^4.3.2", 24 | "eslint": "^9.11.1", 25 | "eslint-plugin-react": "^7.37.0", 26 | "eslint-plugin-react-hooks": "^5.1.0-rc.0", 27 | "eslint-plugin-react-refresh": "^0.4.12", 28 | "globals": "^15.9.0", 29 | "vite": "^5.4.8" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import react from 'eslint-plugin-react' 4 | import reactHooks from 'eslint-plugin-react-hooks' 5 | import reactRefresh from 'eslint-plugin-react-refresh' 6 | 7 | export default [ 8 | { ignores: ['dist'] }, 9 | { 10 | files: ['**/*.{js,jsx}'], 11 | languageOptions: { 12 | ecmaVersion: 2020, 13 | globals: globals.browser, 14 | parserOptions: { 15 | ecmaVersion: 'latest', 16 | ecmaFeatures: { jsx: true }, 17 | sourceType: 'module', 18 | }, 19 | }, 20 | settings: { react: { version: '18.3' } }, 21 | plugins: { 22 | react, 23 | 'react-hooks': reactHooks, 24 | 'react-refresh': reactRefresh, 25 | }, 26 | rules: { 27 | ...js.configs.recommended.rules, 28 | ...react.configs.recommended.rules, 29 | ...react.configs['jsx-runtime'].rules, 30 | ...reactHooks.configs.recommended.rules, 31 | 'react/jsx-no-target-blank': 'off', 32 | 'react-refresh/only-export-components': [ 33 | 'warn', 34 | { allowConstantExport: true }, 35 | ], 36 | }, 37 | }, 38 | ] 39 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import ExpenseForm from './components/ExpenseForm'; 3 | import ExpenseList from './components/ExpenseList'; 4 | import TotalExpenses from './components/TotalExpenses'; 5 | 6 | const App = () => { 7 | const [expenses, setExpenses] = useState([]); 8 | const [filterCategory, setFilterCategory] = useState(''); 9 | 10 | useEffect(() => { 11 | const storedExpenses = JSON.parse(localStorage.getItem('expenses')) || []; 12 | setExpenses(storedExpenses); 13 | }, []); 14 | 15 | useEffect(() => { 16 | localStorage.setItem('expenses', JSON.stringify(expenses)); 17 | }, [expenses]); 18 | 19 | const addExpense = (expense) => { 20 | setExpenses([...expenses, expense]); 21 | }; 22 | 23 | return ( 24 |
25 |
26 |

Expense Tracker

27 | 28 | setFilterCategory(e.target.value)} 32 | className="w-full p-2 mt-4 mb-6 border border-gray-300 rounded shadow-sm focus:outline-none focus:border-pink-400" 33 | /> 34 | 35 | 36 |
37 |
38 | ); 39 | }; 40 | 41 | export default App; 42 | -------------------------------------------------------------------------------- /src/components/ExpenseForm.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | const ExpenseForm = ({ addExpense }) => { 4 | const [amount, setAmount] = useState(''); 5 | const [category, setCategory] = useState(''); 6 | const [date, setDate] = useState(''); 7 | const [description, setDescription] = useState(''); 8 | 9 | const handleSubmit = (e) => { 10 | e.preventDefault(); 11 | if (amount && category && date) { 12 | addExpense({ amount, category, date, description }); 13 | setAmount(''); 14 | setCategory(''); 15 | setDate(''); 16 | setDescription(''); 17 | } 18 | }; 19 | 20 | return ( 21 |
22 | setAmount(e.target.value)} 27 | className="w-full p-3 border rounded-lg shadow-sm focus:outline-none focus:border-pink-400" 28 | required 29 | /> 30 | setCategory(e.target.value)} 35 | className="w-full p-3 border rounded-lg shadow-sm focus:outline-none focus:border-pink-400" 36 | required 37 | /> 38 | setDate(e.target.value)} 42 | className="w-full p-3 border rounded-lg shadow-sm focus:outline-none focus:border-pink-400" 43 | required 44 | /> 45 | 51 | 54 |
55 | ); 56 | }; 57 | 58 | export default ExpenseForm; 59 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------