diff --git a/client/src/components/AppBar.jsx b/client/src/components/AppBar.jsx index 76134ac..a598256 100644 --- a/client/src/components/AppBar.jsx +++ b/client/src/components/AppBar.jsx @@ -1,50 +1,65 @@ -import AppBar from '@mui/material/AppBar'; -import Box from '@mui/material/Box'; -import Toolbar from '@mui/material/Toolbar'; -import Typography from '@mui/material/Typography'; -import Button from '@mui/material/Button'; -import { Link } from 'react-router-dom'; -import { useDispatch, useSelector } from 'react-redux'; -import { setMessage } from '../reducers/messageReducer'; +import AppBar from "@mui/material/AppBar"; +import Box from "@mui/material/Box"; +import Toolbar from "@mui/material/Toolbar"; +import Typography from "@mui/material/Typography"; +import Button from "@mui/material/Button"; +import { Link } from "react-router-dom"; +import { useDispatch, useSelector } from "react-redux"; +import { setMessage } from "../reducers/messageReducer"; const linkStyle = { - textDecoration: 'none', - color: 'white', - cursor: 'pointer' -} + textDecoration: "none", + color: "white", + cursor: "pointer", +}; export default function ButtonAppBar() { - const user = useSelector(state => state.auth.user) - const dispatch = useDispatch() + const user = useSelector((state) => state.auth.user); + const dispatch = useDispatch(); const logout = () => { - localStorage.removeItem('expenseTrackerToken') - dispatch(setMessage(['User logged out', true])) - setTimeout(() => dispatch(setMessage(null)), 5000) - } + localStorage.removeItem("expenseTrackerToken"); + dispatch(setMessage(["User logged out", true])); + setTimeout(() => dispatch(setMessage(null)), 5000); + }; return ( - + - - Expense Tracker + + Expense Tracker + - {user && - <> - - - - } + {user && ( + <> + + + + )} - {!user && - <> - - - - } + {!user && ( + <> + + + + )} diff --git a/client/src/components/TransactionList.jsx b/client/src/components/TransactionList.jsx index 669e06c..e1ab159 100644 --- a/client/src/components/TransactionList.jsx +++ b/client/src/components/TransactionList.jsx @@ -1,97 +1,113 @@ -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableContainer from '@mui/material/TableContainer'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import Paper from '@mui/material/Paper'; -import { IconButton, Typography } from '@mui/material'; -import DeleteIcon from '@mui/icons-material/Delete'; -import EditIcon from '@mui/icons-material/Edit'; -import transactionService from '../requests/Transaction.js' -import dayjs from 'dayjs'; -import { useDispatch, useSelector } from 'react-redux'; -import { removeTransaction } from '../reducers/transactionReducer.js'; -import { setEditTransaction } from '../reducers/editTransactionReducer.js'; -import { setMessage } from '../reducers/messageReducer.js'; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Paper from "@mui/material/Paper"; +import { IconButton, Typography } from "@mui/material"; +import DeleteIcon from "@mui/icons-material/Delete"; +import EditIcon from "@mui/icons-material/Edit"; +import transactionService from "../requests/Transaction.js"; +import dayjs from "dayjs"; +import { useDispatch, useSelector } from "react-redux"; +import { removeTransaction } from "../reducers/transactionReducer.js"; +import { setEditTransaction } from "../reducers/editTransactionReducer.js"; +import { setMessage } from "../reducers/messageReducer.js"; export default function BasicTable() { - - const dispatch = useDispatch() - const transactions = useSelector(state=>state.transactions) - const sortedTrans = transactions.slice().sort((a, b) => a.date > b.date ? -1 : 1) - + const dispatch = useDispatch(); + const transactions = useSelector((state) => state.transactions); + const sortedTrans = transactions + .slice() + .sort((a, b) => (a.date > b.date ? -1 : 1)); const handleEdit = (transaction) => { - dispatch(setEditTransaction(transaction)) - return - } + dispatch(setEditTransaction(transaction)); + return; + }; const handleDelete = async (id) => { - if (confirm("Are you sure you want to delete the transaction?")){ - const res = await transactionService.remove(id) - dispatch(removeTransaction(id)) - if(res.status===204){ - dispatch(setMessage(['Deleted Successfully',true])) - setTimeout(()=>dispatch(setMessage(null)),5000) + console.log(id); + if (confirm("Are you sure you want to delete the transaction?")) { + const res = await transactionService.remove(id); + console.log(res); + dispatch(removeTransaction(id)); + if (res.status === 204) { + dispatch(setMessage(["Deleted Successfully", true])); + setTimeout(() => dispatch(setMessage(null)), 5000); } } - } + }; - if(!sortedTrans.length>0){ - return( - + if (!sortedTrans.length > 0) { + return ( + No data to display - - ) + + ); } return ( <> - - + List of transactions - - + + - Amount - Description - Category - Date - Action + + Amount + + + Description + + + Category + + + Date + + + Action + {sortedTrans.map((row) => ( {row.amount} {row.description} {row.category} - {dayjs(row.date).format('MMMM D, YYYY')} - + {dayjs(row.date).format("MMMM D, YYYY")} + + handleEdit()} - > - + color="primary" + sx={{ marginLeft: 1, cursor: "pointer" }} + onClick={() => { + handleEdit(row); + }} + > + handleDelete(row.id)} - sx={{ cursor: 'pointer' }}> + color="error" + onClick={() => { + handleDelete(row.id); + }} + sx={{ cursor: "pointer" }} + > - ))} @@ -100,4 +116,4 @@ export default function BasicTable() { ); -} \ No newline at end of file +} diff --git a/client/src/pages/Category.jsx b/client/src/pages/Category.jsx index 7897de1..b402964 100644 --- a/client/src/pages/Category.jsx +++ b/client/src/pages/Category.jsx @@ -1,66 +1,69 @@ -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableContainer from '@mui/material/TableContainer'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import Paper from '@mui/material/Paper'; -import { IconButton, Typography } from '@mui/material'; -import DeleteIcon from '@mui/icons-material/Delete'; -import { useDispatch, useSelector } from 'react-redux'; -import {setUser} from '../reducers/authReducer' -import CategoryService from '../requests/Category.js' -import CategoryForm from '../components/CategoryForm.jsx' -import { setMessage } from '../reducers/messageReducer'; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Paper from "@mui/material/Paper"; +import { IconButton, Typography } from "@mui/material"; +import DeleteIcon from "@mui/icons-material/Delete"; +import { useDispatch, useSelector } from "react-redux"; +import { setUser } from "../reducers/authReducer"; +import CategoryService from "../requests/Category.js"; +import CategoryForm from "../components/CategoryForm.jsx"; +import { setMessage } from "../reducers/messageReducer"; export default function Category() { + const dispatch = useDispatch(); - const dispatch = useDispatch() - - const categories = useSelector(state=>state.auth.user.categories) + const categories = useSelector((state) => state.auth.user.categories); const handleDelete = async (name) => { - if (confirm("Are you sure you want to delete the category?")){ - const res = await CategoryService.remove(name) + if (confirm("Are you sure you want to delete the category?")) { + const res = await CategoryService.remove(name); // setTransactions(transactions.filter(trans => trans.id != id)) - if(res.status===200){ - dispatch(setUser(res.data)) - dispatch(setMessage(['Category deleted successfully',true])) - setTimeout(()=>dispatch(setMessage(null)),5000) + if (res.status === 200) { + dispatch(setUser(res.data)); + dispatch(setMessage(["Category deleted successfully", true])); + setTimeout(() => dispatch(setMessage(null)), 5000); } } - } + }; return ( <> - - - + + List of categories
- Name - Action + + Name + + + Action + {categories.map((row) => ( {row} - handleDelete(row)} - sx={{ cursor: 'pointer' }}> + color="error" + onClick={() => { + handleDelete(row); + }} + sx={{ cursor: "pointer" }} + > - ))} @@ -69,4 +72,4 @@ export default function Category() { ); -} \ No newline at end of file +} diff --git a/client/src/pages/Login.jsx b/client/src/pages/Login.jsx index 8520b97..7532fe6 100644 --- a/client/src/pages/Login.jsx +++ b/client/src/pages/Login.jsx @@ -1,29 +1,34 @@ -import Avatar from '@mui/material/Avatar'; -import Button from '@mui/material/Button'; -import CssBaseline from '@mui/material/CssBaseline'; -import TextField from '@mui/material/TextField'; -import { Link, useNavigate } from 'react-router-dom'; -import Grid from '@mui/material/Grid'; -import Box from '@mui/material/Box'; -import LockOutlinedIcon from '@mui/icons-material/LockOutlined'; -import Typography from '@mui/material/Typography'; -import Container from '@mui/material/Container'; -import { createTheme, ThemeProvider } from '@mui/material/styles'; -import accountService from '../requests/Account' +import Avatar from "@mui/material/Avatar"; +import Button from "@mui/material/Button"; +import CssBaseline from "@mui/material/CssBaseline"; +import TextField from "@mui/material/TextField"; +import { Link, useNavigate } from "react-router-dom"; +import Grid from "@mui/material/Grid"; +import Box from "@mui/material/Box"; +import LockOutlinedIcon from "@mui/icons-material/LockOutlined"; +import Typography from "@mui/material/Typography"; +import Container from "@mui/material/Container"; +import { createTheme, ThemeProvider } from "@mui/material/styles"; +import accountService from "../requests/Account"; -import { setUser } from '../reducers/authReducer'; -import {setMessage} from '../reducers/messageReducer' -import { useDispatch } from 'react-redux'; +import { setUser } from "../reducers/authReducer"; +import { setMessage } from "../reducers/messageReducer"; +import { useDispatch } from "react-redux"; function Copyright(props) { return ( - - {'Copyright © '} + + {"Copyright © "} Expense Tracker - {' '} + {" "} {new Date().getFullYear()} - {'.'} + {"."} ); } @@ -32,29 +37,27 @@ function Copyright(props) { const defaultTheme = createTheme(); - export default function SignIn() { - const navigate = useNavigate() - const dispatch = useDispatch() + const navigate = useNavigate(); + const dispatch = useDispatch(); const handleSubmit = async (event) => { event.preventDefault(); const data = new FormData(event.currentTarget); const credentials = { - email: data.get('email'), - password: data.get('password'), - } + email: data.get("email"), + password: data.get("password"), + }; try { - const res = await accountService.login(credentials) - dispatch(setUser(res.user)) - navigate('/') - dispatch(setMessage(['Login success', true])) + const res = await accountService.login(credentials); + dispatch(setUser(res.user)); + navigate("/"); + dispatch(setMessage(["Login success", true])); } catch (error) { - dispatch(setMessage([error.response.data, false])) + dispatch(setMessage([error.response.data, false])); } - setTimeout(() => dispatch(setMessage(null)), 5000) - + setTimeout(() => dispatch(setMessage(null)), 5000); }; return ( @@ -63,18 +66,23 @@ export default function SignIn() { - + Sign in - + - + {"Don't have an account? Sign Up"} @@ -116,4 +124,4 @@ export default function SignIn() { ); -} \ No newline at end of file +} diff --git a/client/src/requests/Category.js b/client/src/requests/Category.js index 2886176..b465d97 100644 --- a/client/src/requests/Category.js +++ b/client/src/requests/Category.js @@ -1,21 +1,28 @@ import axios from "axios"; -const baseurl = 'http://localhost:4000/category/' +const baseurl = "http://localhost:4000/category/"; -const headers = (token)=> { - return {authorization:`bearer ${token}`} -} +const headers = (token) => { + return { authorization: `bearer ${token}` }; +}; +const remove = async (catName) => { + console.log(catName); + const token = localStorage.getItem("expenseTrackerToken"); + console.log(`${baseurl}${catName}`); + const res = await axios.delete(`${baseurl}${catName}`, { + headers: headers(token), + }); + return res; +}; +const create = async (catName) => { + const token = localStorage.getItem("expenseTrackerToken"); + const res = await axios.post( + baseurl, + { name: catName }, + { headers: headers(token) } + ); + return res; +}; -const remove = async (catName)=>{ - const token = localStorage.getItem('expenseTrackerToken') - const res = await axios.delete(baseurl+catName,{headers:headers(token)}) - return res -} -const create = async (catName)=>{ - const token = localStorage.getItem('expenseTrackerToken') - const res = await axios.post(baseurl,{name:catName},{headers:headers(token)}) - return res -} - -export default {remove,create} \ No newline at end of file +export default { remove, create }; diff --git a/client/src/requests/Transaction.js b/client/src/requests/Transaction.js index ec71dd2..35429ef 100644 --- a/client/src/requests/Transaction.js +++ b/client/src/requests/Transaction.js @@ -1,34 +1,41 @@ import axios from "axios"; -const baseurl = 'http://localhost:4000/transactions/' +const baseurl = "http://localhost:4000/transactions/"; -const headers = (token)=> { - return {authorization:`bearer ${token}`} -} +const headers = (token) => { + return { authorization: `bearer ${token}` }; +}; const create = async (transaction) => { - const token = localStorage.getItem('expenseTrackerToken') - const res = await axios.post(baseurl,transaction,{headers:headers(token)}) - return res.data -} - -const fetchAll = async ()=>{ - const token = localStorage.getItem('expenseTrackerToken') - const res =await axios.get(baseurl,{headers:headers(token)}) - return res.data -} - + const token = localStorage.getItem("expenseTrackerToken"); + const res = await axios.post(baseurl, transaction, { + headers: headers(token), + }); + return res.data; +}; + +const fetchAll = async () => { + const token = localStorage.getItem("expenseTrackerToken"); + const res = await axios.get(baseurl, { headers: headers(token) }); + return res.data; +}; const remove = async (id) => { - const token = localStorage.getItem('expenseTrackerToken') - const res = await axios.delete(`${baseurl}${id}`,{headers:headers(token)}) - return res -} - -const update = async (transaction)=>{ - const token = localStorage.getItem('expenseTrackerToken') - const res = await axios.put(baseurl+transaction.id,transaction,{headers:headers(token)}) - return res.data -} - -export default {create,fetchAll,remove,update} \ No newline at end of file + console.log(id); + const token = localStorage.getItem(id); + console.log(`${baseurl}/transactions${id}`); + const res = await axios.delete(`${baseurl}${id}`, { + headers: headers(token), + }); + return res; +}; + +const update = async (transaction) => { + const token = localStorage.getItem("expenseTrackerToken"); + const res = await axios.put(baseurl + transaction.id, transaction, { + headers: headers(token), + }); + return res.data; +}; + +export default { create, fetchAll, remove, update }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..00c7400 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "ExpenseTracker-Debugathon", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/server/routes/category.js b/server/routes/category.js index 0186b6e..b059732 100644 --- a/server/routes/category.js +++ b/server/routes/category.js @@ -1,22 +1,31 @@ import { Router } from "express"; -const router = Router() -import User from '../models/User.js' +const router = Router(); +import User from "../models/User.js"; -router.delete('/:name',async (req,res)=>{ - req.user.categories.filter(n=>n!==req.params.name) - const user = await User.findByIdAndUpdate(req.user.id,{categories:req.user.categories},{new:true}) - res.status(200).json(user) -}) +router.delete("/:name", async (req, res) => { + console.log(req.params.name); + req.user.categories.filter((n) => n !== req.params.name); + const user = await User.findByIdAndDelete( + req.user.id, + { categories: req.user.categories }, + { new: true } + ); + res.status(200).json(user); +}); -router.post('/',async (req,res)=>{ - var updatedCat = [] - if(!req.user.categories.includes(req.body.name)){ - updatedCat = req.user.categories.concat(req.body.name) - }else{ - updatedCat = req.user.categories +router.post("/", async (req, res) => { + var updatedCat = []; + if (!req.user.categories.includes(req.body.name)) { + updatedCat = req.user.categories.concat(req.body.name); + } else { + updatedCat = req.user.categories; } - const user = await User.findByIdAndUpdate(req.user.id,{categories:updatedCat},{new:true}) - res.status(200).json(user) -}) + const user = await User.findByIdAndUpdate( + req.user.id, + { categories: updatedCat }, + { new: true } + ); + res.status(200).json(user); +}); -export default router \ No newline at end of file +export default router; diff --git a/server/routes/transactions.js b/server/routes/transactions.js index 9991b55..ed3ca54 100644 --- a/server/routes/transactions.js +++ b/server/routes/transactions.js @@ -1,42 +1,44 @@ -import { Router } from "express" -import Transaction from "../models/Transaction.js" - -const router = Router() - -router.get('/', async (req, res) => { - const userId = req.user.id - const allTransaction = await Transaction.find({user:userId}) - return res.json(allTransaction) -}) - -router.post('/', async (req, res) => { - const newTrans = Transaction({ ...req.body, user: req.user.id }) - const newT = newTrans.save() - return res.json(newT) -}) - -router.put('/:id', async (req, res) => { - const id = req.params.id - const trans = await Transaction.findById(id) - const updatedTrans = await Transaction.findByIdAndUpdate(id, req.body) - res.json(updatedTrans) -}) - -router.delete('/:id', async (req, res) => { - const id = req.params.id - const trans = await Transaction.findById(id) - - if(!trans){ - return res.sendStatus(404).end() +import { Router } from "express"; +import Transaction from "../models/Transaction.js"; + +const router = Router(); + +router.get("/", async (req, res) => { + const userId = req.user.id; + const allTransaction = await Transaction.find({ user: userId }); + return res.json(allTransaction); +}); + +router.post("/", async (req, res) => { + const newTrans = Transaction({ ...req.body, user: req.user.id }); + const newT = newTrans.save(); + return res.json(newT); +}); + +router.put("/:id", async (req, res) => { + const id = req.params.id; + const trans = await Transaction.findById(id); + const updatedTrans = await Transaction.findByIdAndUpdate(id, req.body); + res.json(updatedTrans); +}); + +router.delete("/:id", async (req, res) => { + const id = req.params.id; + console.log("---->" + id); + const trans = await Transaction.findById(id); + + if (!trans) { + return res.sendStatus(404).end(); } if (String(trans.user) === req.user.id) { - await Transaction.findByIdAndDelete(id) - res.sendStatus(204).end() + await Transaction.findByIdAndDelete(id); + res.sendStatus(204).end(); } else { - res.sendStatus(401).json("not authorized to update someone else's transaction") + res + .sendStatus(401) + .json("not authorized to update someone else's transaction"); } -}) +}); - -export default router \ No newline at end of file +export default router;