diff --git a/client/src/assets/spinner.gif b/client/src/assets/spinner.gif
new file mode 100644
index 0000000..0424aa4
Binary files /dev/null and b/client/src/assets/spinner.gif differ
diff --git a/client/src/components/Layout/AddWallet/AddWallet.jsx b/client/src/components/Layout/AddWallet/AddWallet.jsx
index 39bf772..f33e1fb 100644
--- a/client/src/components/Layout/AddWallet/AddWallet.jsx
+++ b/client/src/components/Layout/AddWallet/AddWallet.jsx
@@ -22,7 +22,7 @@ import AppContext from '../../../context/app/context';
// api
import { postWallets } from '../../../utils/http-request';
-// import getWallets from '../../../utils/http-request';
+import { getWallets } from '../../../utils/http-request';
const walletSchema = z.object({
name: z.string(),
@@ -46,7 +46,7 @@ const AddWallet = ({ open, handleClose }) => {
setWallets(response);
};
fetchWallets();
- }, [wallets]);
+ }, []);
// useEffect(() => {
// setWallets(appContext.wallets);
diff --git a/client/src/components/Layout/Sidebar/SidebarDesktop.jsx b/client/src/components/Layout/Sidebar/SidebarDesktop.jsx
index 4a8efe6..6aa7934 100644
--- a/client/src/components/Layout/Sidebar/SidebarDesktop.jsx
+++ b/client/src/components/Layout/Sidebar/SidebarDesktop.jsx
@@ -7,7 +7,7 @@ import { AiOutlineCaretRight, AiOutlineMenu } from 'react-icons/ai';
const buckets = [
{
- name: 'Sign In',
+ name: 'About us',
color: '#FDD652',
},
];
@@ -32,7 +32,6 @@ const Sidebar = ({
isMinimized,
setIsMinimized,
}) => {
- // const [isMinimized, setIsMinimized] = useState(false);
const toggleIsMinimized = () => {
setIsMinimized(!isMinimized);
@@ -50,7 +49,6 @@ const Sidebar = ({
color: 'gray ',
zIndex: '10',
backgroundColor: '#f5f6f7',
- // backgroundColor: 'background.dark',
boxShadow: '0 0 10px rgba(0,0,0,0.2)',
transition: 'width 0.2s',
minWidth: '50px',
diff --git a/client/src/components/Layout/Spinner/Spinner.jsx b/client/src/components/Layout/Spinner/Spinner.jsx
new file mode 100644
index 0000000..5caf0e6
--- /dev/null
+++ b/client/src/components/Layout/Spinner/Spinner.jsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import spinner from '../../../assets/spinner.gif';
+
+const Spinner = () => {
+ return (
+
+
+
+ );
+};
+
+export default Spinner;
diff --git a/client/src/components/Layout/wrapper/LayoutWrapper.jsx b/client/src/components/Layout/wrapper/LayoutWrapper.jsx
index fc2677a..cff15e2 100644
--- a/client/src/components/Layout/wrapper/LayoutWrapper.jsx
+++ b/client/src/components/Layout/wrapper/LayoutWrapper.jsx
@@ -33,7 +33,7 @@ const LayoutWrapper = ({ children }) => {
>
- {children}
+ {children}
);
diff --git a/client/src/components/general/TableData/TableData.css b/client/src/components/general/TableData/TableData.css
deleted file mode 100644
index e69de29..0000000
diff --git a/client/src/components/general/TableData/TableData.jsx b/client/src/components/general/TableData/TableData.jsx
index da54d4c..9a2a02c 100644
--- a/client/src/components/general/TableData/TableData.jsx
+++ b/client/src/components/general/TableData/TableData.jsx
@@ -1,377 +1,54 @@
import * as React from 'react';
-import PropTypes from 'prop-types';
-import { alpha } from '@mui/material/styles';
-import Box from '@mui/material/Box';
-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 TablePagination from '@mui/material/TablePagination';
-import TableRow from '@mui/material/TableRow';
-import TableSortLabel from '@mui/material/TableSortLabel';
-import Toolbar from '@mui/material/Toolbar';
-import Typography from '@mui/material/Typography';
-import Paper from '@mui/material/Paper';
-import Checkbox from '@mui/material/Checkbox';
-import IconButton from '@mui/material/IconButton';
-import Tooltip from '@mui/material/Tooltip';
-import FormControlLabel from '@mui/material/FormControlLabel';
-import Switch from '@mui/material/Switch';
-import DeleteIcon from '@mui/icons-material/Delete';
-import FilterListIcon from '@mui/icons-material/FilterList';
-import { Button } from '@mui/material';
-import { visuallyHidden } from '@mui/utils';
+import Chip from '@mui/material/Chip';
+import MUIDataTable, { TableFilterList } from 'mui-datatables';
+import { formatDate } from '../../../utils/formatDate';
-
-function descendingComparator(a, b, orderBy) {
- if (b[orderBy] < a[orderBy]) {
- return -1;
- }
- if (b[orderBy] > a[orderBy]) {
- return 1;
- }
- return 0;
-}
-
-function getComparator(order, orderBy) {
- return order === 'desc'
- ? (a, b) => descendingComparator(a, b, orderBy)
- : (a, b) => -descendingComparator(a, b, orderBy);
-}
-
-function stableSort(array, comparator) {
- const stabilizedThis = array.map((el, index) => [el, index]);
- stabilizedThis.sort((a, b) => {
- const order = comparator(a[0], b[0]);
- if (order !== 0) {
- return order;
- }
- return a[1] - b[1];
- });
- return stabilizedThis.map((el) => el[0]);
-}
-
-const headCells = [
+const columns = [
{
- id: 'name',
- numeric: false,
- disablePadding: true,
+ name: 'name',
label: 'Wallet Name',
+ options: {
+ filter: true,
+ sort: true,
+ },
},
{
- id: 'walletID',
- numeric: true,
- disablePadding: false,
- label: 'ID',
- },
- {
- id: 'description',
- numeric: false,
- disablePadding: false,
+ name: 'description',
label: 'Description',
+ options: {
+ filter: false,
+ sort: false,
+ },
},
{
- id: 'createdAt',
- numeric: true,
- disablePadding: false,
- label: 'Date',
+ name: 'createdAt',
+ label: 'Date created',
+ options: {
+ filter: true,
+ sort: true,
+ customBodyRender: (value) => formatDate(value), // Format the date using formatDate function
+ },
},
];
-function EnhancedTableHead({
- onSelectAllClick,
- order,
- orderBy,
- numSelected,
- rowCount,
- onRequestSort,
- data,
-}) {
- const createSortHandler = (property) => (event) => {
- onRequestSort(event, property);
- };
-
- return (
-
-
-
- 0 && numSelected < rowCount}
- checked={rowCount > 0 && numSelected === rowCount}
- onChange={onSelectAllClick}
- inputProps={{
- 'aria-label': 'select all desserts',
- }}
- />
-
- {headCells.map((headCell) => (
-
-
- {headCell.label}
- {orderBy === headCell.id ? (
-
- {order === 'desc' ? 'sorted descending' : 'sorted ascending'}
-
- ) : null}
-
-
- ))}
-
-
- );
-}
-
-EnhancedTableHead.propTypes = {
- numSelected: PropTypes.number.isRequired,
- onRequestSort: PropTypes.func.isRequired,
- onSelectAllClick: PropTypes.func.isRequired,
- order: PropTypes.oneOf(['asc', 'desc']).isRequired,
- orderBy: PropTypes.string.isRequired,
- rowCount: PropTypes.number.isRequired,
- data: PropTypes.array.isRequired,
+const options = {
+ filterType: 'checkbox',
+ textLabels: {
+ body: {
+ noMatch: 'Loading wallets... hihi ^^',
+ },
+ },
};
-function EnhancedTableToolbar({ numSelected, data}) {
- const [row, setRow] = React.useState([...data]);
- const [selected, setSelected] = React.useState([]);
- const handleDelete = () => {
- // const updatedRows = row.filter((e) => !selected.includes(e.name));
- // setRow(updatedRows);
- // setSelected([]);
- };
-
+const TableData = ({ data }) => {
return (
- 0 && {
- bgcolor: (theme) =>
- alpha(
- theme.palette.primary.main,
- theme.palette.action.activatedOpacity
- ),
- }),
- }}
- >
- {numSelected > 0 ? (
-
- {numSelected} selected
-
- ) : (
-
- Tracking
-
- )}
-
- {numSelected > 0 ? (
-
-
-
-
-
- ) : (
-
-
-
-
-
- )}
-
+
);
-}
-
-EnhancedTableToolbar.propTypes = {
- numSelected: PropTypes.number.isRequired,
- data: PropTypes.array.isRequired,
};
-export default function EnhancedTable({ data }) {
- const [order, setOrder] = React.useState('asc');
- const [orderBy, setOrderBy] = React.useState('id');
- const [selected, setSelected] = React.useState([]);
- const [page, setPage] = React.useState(0);
- const [dense, setDense] = React.useState(false);
- const [rowsPerPage, setRowsPerPage] = React.useState(5);
-
- const handleRequestSort = (event, property) => {
- const isAsc = orderBy === property && order === 'asc';
- setOrder(isAsc ? 'desc' : 'asc');
- setOrderBy(property);
- };
-
- const handleSelectAllClick = (event) => {
- if (event.target.checked) {
- const newSelected = data.map((n) => n.name);
- setSelected(newSelected);
- return;
- }
- setSelected([]);
- };
-
- const handleClick = (event, name) => {
- const selectedIndex = selected.indexOf(name);
- let newSelected = [];
-
- if (selectedIndex === -1) {
- newSelected = newSelected.concat(selected, name);
- } else if (selectedIndex === 0) {
- newSelected = newSelected.concat(selected.slice(1));
- } else if (selectedIndex === selected.length - 1) {
- newSelected = newSelected.concat(selected.slice(0, -1));
- } else if (selectedIndex > 0) {
- newSelected = newSelected.concat(
- selected.slice(0, selectedIndex),
- selected.slice(selectedIndex + 1)
- );
- }
-
- setSelected(newSelected);
- };
-
- const handleChangePage = (event, newPage) => {
- setPage(newPage);
- };
-
- const handleChangeRowsPerPage = (event) => {
- setRowsPerPage(parseInt(event.target.value, 10));
- setPage(0);
- };
-
- const handleChangeDense = (event) => {
- setDense(event.target.checked);
- };
-
- const isSelected = (name) => selected.indexOf(name) !== -1;
-
- // Avoid a layout jump when reaching the last page with empty rows.
- const emptyRows =
- page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0;
-
- const visibleRows = React.useMemo(
- () =>
- stableSort(data, getComparator(order, orderBy)).slice(
- page * rowsPerPage,
- page * rowsPerPage + rowsPerPage
- ),
- [order, orderBy, page, rowsPerPage]
- );
-
- // const selectedRows = visibleRows.filter((row) =>
- // newSelected.includes(row.name)
- // );
- // setSelectedRows(selectedRows);
-
- return (
-
-
-
-
-
-
-
- {visibleRows.map((row, index) => {
- const isItemSelected = isSelected(row.name);
- const labelId = `enhanced-table-checkbox-${index}`;
-
- return (
- handleClick(event, row.name)}
- role='checkbox'
- aria-checked={isItemSelected}
- tabIndex={-1}
- key={row.name}
- selected={isItemSelected}
- sx={{ cursor: 'pointer' }}
- >
-
-
-
-
- {row.name}
-
- {row.walletID}
- {row.description}
- {row.createdAt}
-
- );
- })}
- {emptyRows > 0 && (
-
-
-
- )}
-
-
-
-
-
- }
- label='Dense padding'
- />
-
- );
-}
+export default TableData;
diff --git a/client/src/components/general/TransactionAdd.jsx b/client/src/components/general/TransactionAdd.jsx
index 4da97a5..960a44d 100644
--- a/client/src/components/general/TransactionAdd.jsx
+++ b/client/src/components/general/TransactionAdd.jsx
@@ -48,13 +48,11 @@ const TransactionAdd = ({ open, handleClose }) => {
const fetchWallets = async () => {
const response = await getWallets();
setWallets(response);
- // await appContext.getWallets();
- // setWallets(appContext.wallets);
};
fetchWallets();
- }, [wallets]);
+ }, []);
-
+ //fetch transactions
useEffect(() => {
const fetchTransactions = async () => {
const response = await getTransactions();
diff --git a/client/src/components/general/table/CustomTable.jsx b/client/src/components/general/table/CustomTable.jsx
index 77470e5..c2f5416 100644
--- a/client/src/components/general/table/CustomTable.jsx
+++ b/client/src/components/general/table/CustomTable.jsx
@@ -1,43 +1,45 @@
-import * as React from "react";
-import Chip from "@mui/material/Chip";
-import MUIDataTable, { TableFilterList } from "mui-datatables";
+import * as React from 'react';
+import Chip from '@mui/material/Chip';
+import MUIDataTable, { TableFilterList } from 'mui-datatables';
+import appContext from '../../../context/app/context';
+import Spinner from '../../Layout/Spinner/Spinner';
const columns = [
{
- name: "walletName",
- label: "Wallet Name",
+ name: 'walletName',
+ label: 'Wallet Name',
options: {
filter: true,
sort: true,
},
},
{
- name: "amount",
- label: "Amount",
+ name: 'amount',
+ label: 'Amount',
options: {
filter: true,
sort: true,
},
},
{
- name: "createdAt",
- label: "Date created",
+ name: 'createdAt',
+ label: 'Date created',
options: {
filter: true,
sort: true,
},
},
{
- name: "description",
- label: "Description",
+ name: 'description',
+ label: 'Description',
options: {
filter: false,
sort: false,
},
},
{
- name: "type",
- label: "Type",
+ name: 'type',
+ label: 'Type',
options: {
filter: true,
sort: true,
@@ -46,15 +48,20 @@ const columns = [
];
const options = {
- filterType: "checkbox",
+ filterType: 'checkbox',
+ textLabels: {
+ body: {
+ noMatch: 'Let sip a tea while the transactions are loaded... hihi :)',
+ },
+ },
};
-const CustomTable = ({transactions, loading}) => {
+const CustomTable = ({ transactions }) => {
const data = transactions;
-
+ const { loading } = React.useContext(appContext);
return (
: data}
columns={columns}
options={options}
loading={loading}
diff --git a/client/src/components/pages/dashboard/Dashboard.css b/client/src/components/pages/dashboard/Dashboard.css
new file mode 100644
index 0000000..8ece7d8
--- /dev/null
+++ b/client/src/components/pages/dashboard/Dashboard.css
@@ -0,0 +1,68 @@
+.rubberBand:active {
+ -webkit-animation-name: rubberBand;
+ animation-name: rubberBand;
+ -webkit-animation-duration: 1s;
+ animation-duration: 1s;
+ -webkit-animation-fill-mode: both;
+ animation-fill-mode: both;
+}
+@-webkit-keyframes rubberBand {
+ 0% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+ 30% {
+ -webkit-transform: scale3d(1.25, 0.75, 1);
+ transform: scale3d(1.25, 0.75, 1);
+ }
+ 40% {
+ -webkit-transform: scale3d(0.75, 1.25, 1);
+ transform: scale3d(0.75, 1.25, 1);
+ }
+ 50% {
+ -webkit-transform: scale3d(1.15, 0.85, 1);
+ transform: scale3d(1.15, 0.85, 1);
+ }
+ 65% {
+ -webkit-transform: scale3d(0.95, 1.05, 1);
+ transform: scale3d(0.95, 1.05, 1);
+ }
+ 75% {
+ -webkit-transform: scale3d(1.05, 0.95, 1);
+ transform: scale3d(1.05, 0.95, 1);
+ }
+ 100% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
+@keyframes rubberBand {
+ 0% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+ 30% {
+ -webkit-transform: scale3d(1.25, 0.75, 1);
+ transform: scale3d(1.25, 0.75, 1);
+ }
+ 40% {
+ -webkit-transform: scale3d(0.75, 1.25, 1);
+ transform: scale3d(0.75, 1.25, 1);
+ }
+ 50% {
+ -webkit-transform: scale3d(1.15, 0.85, 1);
+ transform: scale3d(1.15, 0.85, 1);
+ }
+ 65% {
+ -webkit-transform: scale3d(0.95, 1.05, 1);
+ transform: scale3d(0.95, 1.05, 1);
+ }
+ 75% {
+ -webkit-transform: scale3d(1.05, 0.95, 1);
+ transform: scale3d(1.05, 0.95, 1);
+ }
+ 100% {
+ -webkit-transform: scale3d(1, 1, 1);
+ transform: scale3d(1, 1, 1);
+ }
+}
diff --git a/client/src/components/pages/dashboard/Dashboard.jsx b/client/src/components/pages/dashboard/Dashboard.jsx
index 97a1ff0..0fc6f31 100644
--- a/client/src/components/pages/dashboard/Dashboard.jsx
+++ b/client/src/components/pages/dashboard/Dashboard.jsx
@@ -3,23 +3,28 @@ import Section from '../../Layout/Section/Section';
import LineChart from './component/LineChart/LineChart';
import PieCharts from './component/PieChart/PieCharts';
import DBCard from './component/DBCard/DBCard';
-// import CustomTable from '../../general/table/CustomTable';
+import RecentTable from './component/RecentTable/RecentTable';
import { Grid, Stack, Typography, useMediaQuery } from '@mui/material';
-import { HiCurrencyDollar } from 'react-icons/hi';
//api
-import { getTransactions } from '../../../utils/http-request';
-import AddWallet from '../../Layout/AddWallet/AddWallet';
+import { getTransactions, getWallets } from '../../../utils/http-request';
+// import AppContext from '../../../../context/app-context';
//image
import char5 from '../../../assets/char5.svg';
-import pointDown from '../../../assets/pointDown.jpg';
+import { HiCurrencyDollar } from 'react-icons/hi';
import assistant from '../../../assets/assistant.webp';
+//styling
+import './Dashboard.css';
+
const Dashboard = () => {
const [transactionData, setTransactionData] = useState([]);
+ const [wallets, setWallets] = useState([]);
const isSmallScreen = useMediaQuery((theme) => theme.breakpoints.down('sm'));
+ // const { getTransactions } = React.useContext(AppContext);
+ //fetch transaction data
useEffect(() => {
const fetchTransactions = async () => {
try {
@@ -29,10 +34,22 @@ const Dashboard = () => {
console.log(error);
}
};
-
fetchTransactions();
}, []);
+ //fetch wallet data
+ useEffect(() => {
+ const fetchWallets = async () => {
+ try {
+ const wallets = await getWallets();
+ setWallets(wallets);
+ } catch (error) {
+ console.log(error);
+ }
+ };
+ fetchWallets();
+ }, []);
+
//hardcoded data for the charts
const lineData = [
{
@@ -157,6 +174,7 @@ const Dashboard = () => {
value: '23444',
},
];
+
return (
{
spacing={2}
>
{
marginBottom: '20px',
}}
>
-
- Welcome back, Trang!
-
+ {isSmallScreen ? (
+
+ Welcome back, Trang!
+
+ ) : (
+
+ Welcome back, Trang!
+
+ )}
+
{
alignItems: 'center',
height: 'fit-content',
width: '100%',
- padding: '20px',
+ padding: '5px',
background: 'white',
borderRadius: '10px',
marginTop: '20px',
@@ -249,7 +278,7 @@ const Dashboard = () => {
: {
display: 'flex',
flexDirection: 'column',
- justifyContent: 'space-between',
+ justifyContent: 'center',
alignItems: 'center',
width: '100%',
padding: '30px',
@@ -278,10 +307,16 @@ const Dashboard = () => {
>
Recent Transactions
Latest transaction all the time
- {/* */}
+
-
+
diff --git a/client/src/components/pages/dashboard/component/LineChart/LineChart.jsx b/client/src/components/pages/dashboard/component/LineChart/LineChart.jsx
index 1bd890c..95d0f60 100644
--- a/client/src/components/pages/dashboard/component/LineChart/LineChart.jsx
+++ b/client/src/components/pages/dashboard/component/LineChart/LineChart.jsx
@@ -12,12 +12,7 @@ import {
import PropTypes from 'prop-types';
const LineChart = ({ data }) => {
return (
-
+
diff --git a/client/src/components/pages/dashboard/component/PieChart/PieCharts.jsx b/client/src/components/pages/dashboard/component/PieChart/PieCharts.jsx
index 601cc2b..a415d25 100644
--- a/client/src/components/pages/dashboard/component/PieChart/PieCharts.jsx
+++ b/client/src/components/pages/dashboard/component/PieChart/PieCharts.jsx
@@ -20,12 +20,12 @@ const COLORS = [
const PieCharts = ({ pieData }) => {
return (
-
+
{
+ // Convert the data object to an array and slice it to only include the first 5 elements
+ const mergedData = transactionsData.map((transaction) => {
+ const wallet = walletsData.find((wallet) => wallet.id === transaction.walletId);
+ return {
+ name: wallet?.name || 'Unknown Wallet',
+ amount: transaction.amount,
+ };
+ });
+
+ // Sort the mergedData by the most recent transactions
+ const sortedData = mergedData.sort((a, b) => b.createdAt - a.createdAt);
+
+ // Slice the data array to only include the first 5 elements
+ const slicedData = sortedData.slice(0, 5);
+
+ return (
+
+
+
+ View More
+
+
+
+ Transaction
+ Amount
+
+
+
+ {slicedData.map((row) => (
+
+
+ {row.name}
+
+ {row.amount}
+
+ ))}
+
+
+
+ );
+};
+
+export default RecentTable;
diff --git a/client/src/components/pages/transaction/TransactionsPage.jsx b/client/src/components/pages/transaction/TransactionsPage.jsx
index 6dc4604..fa4676c 100644
--- a/client/src/components/pages/transaction/TransactionsPage.jsx
+++ b/client/src/components/pages/transaction/TransactionsPage.jsx
@@ -1,31 +1,126 @@
-import { useContext, useState, useEffect, useCallback } from "react";
-import "./Transactions.css";
-import { Stack } from "@mui/material";
-import CustomTabs from "@/components/general/CustomTabs";
+// import { useContext, useState, useEffect, useCallback } from 'react';
+// import './Transactions.css';
+// import { Stack } from '@mui/material';
+// import CustomTabs from '@/components/general/CustomTabs';
-import Section from "../../Layout/Section/Section";
-import TransactionGridView from "./components/TransactionGridView/TransactionGridView";
+// import Section from '../../Layout/Section/Section';
+// import TransactionGridView from './components/TransactionGridView/TransactionGridView';
+// import CustomTable from '../../general/table/CustomTable';
+// import {
+// formatTransactionList,
+// generateFakeTransactionData,
+// generateFakeWallets,
+// pushTransactions,
+// pushWallets,
+// } from '@/utils/helper';
+// // import { getTransactions } from "@/utils/http-request";
+// import AppContext from '@/context/app/context';
+
+// const TransactionPage = () => {
+// const [activeTab, setView] = useState(0);
+// const [transactionData, setTransactionData] = useState([]);
+
+// const { transactions, getTransactions, loading } = useContext(AppContext);
+// const changeView = (event, newView) => {
+// setView(newView);
+// };
+
+// useEffect(() => {
+// const feedData = async () => {
+// const wallets = generateFakeWallets(1);
+// try {
+// await pushWallets(wallets);
+// } catch (error) {
+// console.log(error);
+// }
+
+// try {
+// const trans = await generateFakeTransactionData(1);
+// console.log(trans);
+// await pushTransactions(trans);
+// } catch (error) {
+// console.log(error);
+// }
+// };
+// const fetchTransactions = async () => {
+// try {
+// await getTransactions();
+// // memoize the function
+// const formattedTransaction = await formatTransactionList(transactions);
+
+// setTransactionData(formattedTransaction);
+// } catch (error) {
+// console.log(error);
+// }
+// };
+// if (transactions.length === 0 || transactionData.length === 0) {
+// // feedData();
+// fetchTransactions();
+// }
+// }, [transactionData]);
+
+// const tabs = [
+// {
+// id: 'list',
+// label: 'List',
+// component: ,
+// },
+// {
+// id: 'grid',
+// label: 'Grid',
+// component: ,
+// },
+// ];
+// return (
+//
+//
+//
+//
+//
+//
+//
+// {tabs[activeTab].component}
+//
+//
+// );
+// };
+
+// export default TransactionPage;
+
+import { useContext, useState, useEffect, useCallback } from 'react';
+import './Transactions.css';
+import { Stack } from '@mui/material';
+import CustomTabs from '@/components/general/CustomTabs';
+
+import Section from '../../Layout/Section/Section';
+import TransactionGridView from './components/TransactionGridView/TransactionGridView';
import {
formatTransactionList,
generateFakeTransactionData,
generateFakeWallets,
pushTransactions,
pushWallets,
-} from "@/utils/helper";
+} from '@/utils/helper';
// import { getTransactions } from "@/utils/http-request";
-import AppContext from "@/context/app/context";
-import CustomTable from "../../general/table/CustomTable";
+import AppContext from '@/context/app/context';
+import CustomTable from '../../general/table/CustomTable';
const TransactionPage = () => {
const [activeTab, setView] = useState(0);
const [transactionData, setTransactionData] = useState([]);
-
- const {transactions, getTransactions, loading} = useContext(AppContext);
+
+ const { transactions, getTransactions, loading } = useContext(AppContext);
const changeView = (event, newView) => {
setView(newView);
};
-
-
+
useEffect(() => {
const feedData = async () => {
const wallets = generateFakeWallets(1);
@@ -48,9 +143,8 @@ const TransactionPage = () => {
await getTransactions();
// memoize the function
const formattedTransaction = await formatTransactionList(transactions);
-
+
setTransactionData(formattedTransaction);
-
} catch (error) {
console.log(error);
}
@@ -59,31 +153,28 @@ const TransactionPage = () => {
// feedData();
fetchTransactions();
}
-
-
}, [transactionData]);
-
const tabs = [
{
- id: "list",
- label: "List",
+ id: 'list',
+ label: 'List',
component: ,
},
{
- id: "grid",
- label: "Grid",
+ id: 'grid',
+ label: 'Grid',
component: ,
},
];
return (
-
+
-
+
{
+// const appContext = useContext(AppContext);
+// var [activeTab, setView] = useState(0);
+
+// const changeView = (event, newView) => {
+// setView(newView);
+// };
+
+// const {wallets, loading, getWallets} = appContext;
+
+
+// // CORS
+
+
+// console.log(wallets);
+// useEffect(() => {
+// const fetchWallets = async () => {
+// await getWallets();
+// };
+
+// fetchWallets();
+// }, []); // Use the memoized function as a dependency instead of getWallets
+
+
+// // useEffect(() => {
+// // setWalletData(wallets);
+// // }, [wallets]);
+
+// const tabs = [
+// {
+// id: 'list',
+// label: 'List',
+// component: ,
+// },
+// {
+// id: 'grid',
+// label: 'Grid',
+// component: ,
+// },
+// ];
+
+// return (
+//
+//
+//
+//
+//
+//
+// {tabs[activeTab].component}
+//
+// );
+// };
+
+// export default WalletsPage;
+
import { useEffect, useState, useContext, useCallback } from 'react';
//components
@@ -74,4 +152,4 @@ const WalletsPage = () => {
);
};
-export default WalletsPage;
+export default WalletsPage;
\ No newline at end of file
diff --git a/client/src/components/pages/wallet/components/WalletCard/WalletCard.css b/client/src/components/pages/wallet/components/WalletCard/WalletCard.css
deleted file mode 100644
index 9752ccb..0000000
--- a/client/src/components/pages/wallet/components/WalletCard/WalletCard.css
+++ /dev/null
@@ -1,7 +0,0 @@
-#wallet-card-div {
- display: inline-flex;
- align-items: center;
- justify-content: flex-start;
- text-align: left;
- gap: 60px;
-}
diff --git a/client/src/components/pages/wallet/components/WalletCard/WalletCard.jsx b/client/src/components/pages/wallet/components/WalletCard/WalletCard.jsx
index 892329a..67b74ee 100644
--- a/client/src/components/pages/wallet/components/WalletCard/WalletCard.jsx
+++ b/client/src/components/pages/wallet/components/WalletCard/WalletCard.jsx
@@ -6,9 +6,9 @@ import CardContent from '@mui/material/CardContent';
import CardMedia from '@mui/material/CardMedia';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
-import './WalletCard.css';
+import { Stack } from '@mui/material';
-const WalletCard = ({ name, id, date, description }) => {
+const WalletCard = ({ name, date, description }) => {
// const [walletName, setWalletName] = useState('');
const buttonStyle = {
@@ -34,53 +34,31 @@ const WalletCard = ({ name, id, date, description }) => {
fontFamily: 'Courier New, Courier, monospace',
};
- const dateAmountStyles = {
- margin: 'auto',
- textAlign: 'left',
- fontSize: '11px',
- };
-
return (
-
-
- {name}
-
-
- {/*
- ${amount}
- */}
+
+
+
+ {name}
+
- {date}
+ {description}
-
- {/*
- {balance}
- */}
-
-
- {description}
-
+
{/* category for wallet coming soon */}
diff --git a/client/src/components/pages/wallet/components/WalletsView/WalletContent.jsx b/client/src/components/pages/wallet/components/WalletsView/WalletContent.jsx
index a1aaada..82575ab 100644
--- a/client/src/components/pages/wallet/components/WalletsView/WalletContent.jsx
+++ b/client/src/components/pages/wallet/components/WalletsView/WalletContent.jsx
@@ -9,7 +9,7 @@ const WalletContent = ({ data }) => {
{
}}
key={wallet.id}
>
-
+
))}
diff --git a/client/src/context/app/state.jsx b/client/src/context/app/state.jsx
index 8c738db..d1a5248 100644
--- a/client/src/context/app/state.jsx
+++ b/client/src/context/app/state.jsx
@@ -21,6 +21,13 @@ import
GET_TOTAL_EXPENSES,
GET_TOTAL_BALANCE,
SET_LOADING,
+ AUTH_ERROR,
+ REGISTER_SUCCESS,
+ REGISTER_FAIL,
+ USER_LOADED,
+ LOGIN_SUCCESS,
+ LOGIN_FAIL,
+ LOGOUT,
} from '../types';
const AppState = (props) => {
@@ -34,47 +41,42 @@ const AppState = (props) => {
totalExpenses: [],
totalBalance: [],
error: null,
- loading: true,
+ loading: false,
+ token: localStorage.getItem('token'),
+ isAuthenticated: null,
+ user: null,
};
const [state, dispatch] = useReducer(AppReducer, initialState);
+ const baseUrl = 'http://localhost:3000/api';
+
+ // Load User
+
+ // Register User
+
+ // Login User
+
+ // Logout
+
+ // Clear Errors
// Get Wallets
const getWallets = async () => {
- dispatch({ type: SET_LOADING });
- try {
- console.log(baseUrl);
- const res = await axios.get(baseUrl + '/wallet');
-
- dispatch({
- type: GET_WALLETS,
- payload: res.data,
- });
- } catch (err) {
-
- dispatch({
- type: WALLET_ERROR,
- payload: err.response.msg,
- });
- }
+ setLoading();
+ // try {
+ const res = await axios.get(baseUrl + '/wallet');
+ dispatch({
+ type: GET_WALLETS,
+ payload: res.data,
+ });
+ // } catch (err) {
+ // dispatch({
+ // type: WALLET_ERROR,
+ // payload: err.response.msg,
+ // });
+ // }
};
- // Get Wallets by ID
- // const getWallet = async (id) => {
- // setLoading();
- // try {
- // const res = await axios.get(baseUrl + `/wallet/${id}`);
- // dispatch({
- // type: GET_WALLETS,
- // payload: res.data,
- // });
- // } catch (err) {
- // dispatch({
- // type: WALLET_ERROR,
- // payload: err.response.msg,
- // });
- // }
- // };
// Add Wallet
const addWallet = async (wallet) => {
@@ -101,6 +103,7 @@ const AppState = (props) => {
// Delete Wallet
const deleteWallet = async (id) => {
+ setLoading();
try {
await axios.delete(baseUrl + `/wallet/${id}`);
dispatch({
@@ -207,6 +210,10 @@ const AppState = (props) => {
return (
{
transactionData.push(formatteData);
}
return transactionData;
-};
-
-// Format the list of wallets
-export const formatWalletList = async (data) => {
- let walletData = [];
- for (let i = 0; i < data.length; i++) {
- const wallet = data[i];
- let formatteData = {
- id: wallet.id,
- name: wallet.name,
- // amount: wallet.amount,
- createdAt: formatDate(wallet.createdAt),
- description: wallet.description,
- // type: wallet.type,
- };
- walletData.push(formatteData);
- }
- return walletData;
-};
+};
\ No newline at end of file
diff --git a/server/prisma/migrations/20230720024706_quan_meeting_late/migration.sql b/server/prisma/migrations/20230720024706_quan_meeting_late/migration.sql
deleted file mode 100644
index 207af10..0000000
--- a/server/prisma/migrations/20230720024706_quan_meeting_late/migration.sql
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- Warnings:
-
- - You are about to drop the column `image` on the `Transaction` table. All the data in the column will be lost.
-
-*/
--- AlterTable
-ALTER TABLE "Transaction" DROP COLUMN "image";
diff --git a/server/prisma/migrations/20230721163036_work_di_ma/migration.sql b/server/prisma/migrations/20230721163036_work_di_ma/migration.sql
deleted file mode 100644
index a36b179..0000000
--- a/server/prisma/migrations/20230721163036_work_di_ma/migration.sql
+++ /dev/null
@@ -1,2 +0,0 @@
--- AlterTable
-ALTER TABLE "Transaction" ADD COLUMN "image" TEXT;
diff --git a/server/prisma/schema.prisma b/server/prisma/schema.prisma
index 9740d06..7e7ed36 100644
--- a/server/prisma/schema.prisma
+++ b/server/prisma/schema.prisma
@@ -14,10 +14,13 @@ model User {
id String @id @default(uuid())
email String @unique
name String
+ wallets Wallet[]
}
model Wallet {
id String @id @default(uuid())
+ userId String?
+ user User? @relation(fields: [userId], references: [id])
name String
balance Float?
createdAt DateTime @default(now())
diff --git a/server/src/controllers/transaction.js b/server/src/controllers/transaction.js
index cafdb1e..6c5be89 100644
--- a/server/src/controllers/transaction.js
+++ b/server/src/controllers/transaction.js
@@ -6,45 +6,44 @@ const prisma = require('../db/prisma');
// get all transactions
// endpoint: /api/transaction
const getTransaction = async (req, res) => {
- try {
- const transactions = await prisma.transaction.findMany();
- res.status(200).json(transactions);
- } catch (error) {
- res.status(500).json(error);
- }
+ try {
+ const transactions = await prisma.transaction.findMany();
+ res.status(200).json(transactions);
+ } catch (error) {
+ res.status(500).json(error);
+ }
};
// get transaction by id
// endpoint: /api/transaction/:id
const getTransactionById = async (req, res) => {
- const id = req.params.id;
- try {
- const transaction = await prisma.transaction.findUnique({
- where: {
- id: id
- }
- });
- res.status(200).json(transaction);
- } catch (error) {
- res.status(500).json(error);
- }
+ const id = req.params.id;
+ try {
+ const transaction = await prisma.transaction.findUnique({
+ where: {
+ id: id,
+ },
+ });
+ res.status(200).json(transaction);
+ } catch (error) {
+ res.status(500).json(error);
+ }
};
-
// get all transactions by wallet id
// endpoint: /api/transaction/wallet/:id
const getTransactionByWalletId = async (req, res) => {
- const id = req.params.id;
- try {
- const transactions = await prisma.transaction.findMany({
- where: {
- walletId: id
- }
- });
- res.status(200).json(transactions);
- } catch (error) {
- res.status(500).json(error);
- }
+ const id = req.params.id;
+ try {
+ const transactions = await prisma.transaction.findMany({
+ where: {
+ walletId: id,
+ },
+ });
+ res.status(200).json(transactions);
+ } catch (error) {
+ res.status(500).json(error);
+ }
};
// create transaction
@@ -70,47 +69,47 @@ const createTransaction = async (req, res) => {
// delete transaction
// endpoint: /api/transaction/delete/:id
const deleteTransaction = async (req, res) => {
- const id = req.params.id;
- try {
- const result = await prisma.transaction.delete({
- where: {
- id: id
- }
- });
- res.status(200).json(result);
- } catch (error) {
- res.status(500).json(error);
- }
+ const id = req.params.id;
+ try {
+ const result = await prisma.transaction.delete({
+ where: {
+ id: id,
+ },
+ });
+ res.status(200).json(result);
+ } catch (error) {
+ res.status(500).json(error);
+ }
};
// update transaction
// endpoint: /api/transaction/update/:id
const updateTransaction = async (req, res) => {
- const id = req.params.id;
- const { amount, description, type, walletId } = req.body;
- try {
- const result = await prisma.transaction.update({
- where: {
- id: id
- },
- data: {
- amount: amount,
- description: description,
- type: type,
- walletId: walletId
- }
- });
- res.status(200).json(result);
- } catch (error) {
- res.status(500).json(error);
- }
+ const id = req.params.id;
+ const { amount, description, type, walletId } = req.body;
+ try {
+ const result = await prisma.transaction.update({
+ where: {
+ id: id,
+ },
+ data: {
+ amount: amount,
+ description: description,
+ type: type,
+ walletId: walletId,
+ },
+ });
+ res.status(200).json(result);
+ } catch (error) {
+ res.status(500).json(error);
+ }
};
module.exports = {
- getTransaction,
- getTransactionById,
- getTransactionByWalletId,
- createTransaction,
- deleteTransaction,
- updateTransaction
-}
\ No newline at end of file
+ getTransaction,
+ getTransactionById,
+ getTransactionByWalletId,
+ createTransaction,
+ deleteTransaction,
+ updateTransaction,
+};
diff --git a/server/src/controllers/user.js b/server/src/controllers/user.js
new file mode 100644
index 0000000..7020464
--- /dev/null
+++ b/server/src/controllers/user.js
@@ -0,0 +1,53 @@
+const prisma = require('../db/prisma');
+
+const getUsers = async (req, res) => {
+ try {
+ const users = await prisma.user.findMany();
+ res.json(users);
+ } catch (error) {
+ res.json(error);
+ }
+};
+
+const getUserById = async (req, res) => {
+ try {
+ const { id } = req.params;
+ const user = await prisma.user.findUnique({
+ where: {
+ id: id,
+ },
+ });
+ res.json(user);
+ } catch (error) {
+ res.json(error);
+ }
+};
+
+const createUser = async (req, res) => {
+ try {
+ const { email, password } = req.body;
+ const result = await prisma.user.create({
+ data: {
+ email: email,
+ password: password,
+ },
+ });
+ res.status(201).json(result);
+ } catch (error) {
+ res.json(error);
+ }
+};
+
+const getWalletsByUserId = async (req, res) => {
+ try {
+ const { id } = req.params;
+ const wallets = await prisma.wallet.findMany({
+ where: {
+ id: id,
+ },
+ });
+ res.json(wallets);
+ } catch (error) {
+ res.json(error);
+ }
+};
diff --git a/server/src/controllers/wallet.js b/server/src/controllers/wallet.js
index f6bd3f7..6215f5c 100644
--- a/server/src/controllers/wallet.js
+++ b/server/src/controllers/wallet.js
@@ -1,4 +1,4 @@
-const prisma = require("../db/prisma");
+const prisma = require('../db/prisma');
// get all wallets
//endpoint: /api/wallet
@@ -15,76 +15,77 @@ const getAllWallets = async (req, res) => {
// get wallet by id
// endpoint: /api/wallet/:id
const getWalletById = async (req, res) => {
- const id = req.params.id;
- try {
- const wallet = await prisma.wallet.findUnique({
- where: {
- id: id
- }
- });
- res.status(200).json(wallet);
- } catch (error) {
- res.status(500).json(error);
- }
+ const id = req.params.id;
+ try {
+ const wallet = await prisma.wallet.findUnique({
+ where: {
+ id: id,
+ },
+ });
+ res.status(200).json(wallet);
+ } catch (error) {
+ res.status(500).json(error);
+ }
};
// create wallet
// endpoint: /api/wallet/create
const createWallet = async (req, res) => {
- const name = req.body.name;
- try {
- const result = await prisma.wallet.create({
- data: {
- name: name
- }
- });
- res.status(201).json(result)
- } catch (error) {
- res.status(500).json(error);
- }
+ const { name, description, createdAt } = req.body;
+ try {
+ const result = await prisma.wallet.create({
+ data: {
+ name: name,
+ description: description,
+ createdAt: createdAt,
+ },
+ });
+ res.status(201).json(result);
+ } catch (error) {
+ res.status(500).json(error);
+ }
};
-
// delete wallet
// endpoint: /api/wallet/delete/:id
const deleteWallet = async (req, res) => {
- const id = req.params.id;
- try {
- const result = await prisma.wallet.delete({
- where: {
- id: id
- }
- });
- res.status(201).json(result);
- } catch (error) {
- res.status(500).json(error);
- }
+ const id = req.params.id;
+ try {
+ const result = await prisma.wallet.delete({
+ where: {
+ id: id,
+ },
+ });
+ res.status(201).json(result);
+ } catch (error) {
+ res.status(500).json(error);
+ }
};
// name change
// endpoint: /api/wallet/name/:id
const changeWalletName = async (req, res) => {
- const id = req.params.id;
- const name = req.body.name;
- try {
- const result = await prisma.wallet.update({
- where: {
- id: id
- },
- data: {
- name: name
- }
- });
- res.status(201).json(result);
- } catch (error) {
- res.status(500).json(error);
- }
+ const id = req.params.id;
+ const name = req.body.name;
+ try {
+ const result = await prisma.wallet.update({
+ where: {
+ id: id,
+ },
+ data: {
+ name: name,
+ },
+ });
+ res.status(201).json(result);
+ } catch (error) {
+ res.status(500).json(error);
+ }
};
module.exports = {
- getAllWallets,
- getWalletById,
- createWallet,
- deleteWallet,
- changeWalletName
-};
\ No newline at end of file
+ getAllWallets,
+ getWalletById,
+ createWallet,
+ deleteWallet,
+ changeWalletName,
+};
diff --git a/server/src/index.js b/server/src/index.js
index 37d32dd..96c0eba 100644
--- a/server/src/index.js
+++ b/server/src/index.js
@@ -52,3 +52,4 @@ console.log("Backend is running");
app.listen(PORT, () => {
console.log('Listening on port ' + PORT);
});
+
diff --git a/server/src/router/user.js b/server/src/router/user.js
new file mode 100644
index 0000000..b995cda
--- /dev/null
+++ b/server/src/router/user.js
@@ -0,0 +1,21 @@
+const express = require('express');
+const validate = require('../middleware/validate');
+const userSchema = require('../validations/user.validation');
+
+const { getUser, getUserById, createUser } = require('../controllers/user');
+
+const router = express.Router();
+
+//get all users
+router.get('/', getUser);
+
+//get user by id
+router.get('/:id', getUserById);
+
+//create user
+router.post('/create', validate(userSchema), createUser);
+
+//get wallets by user id
+router.get('/:id/wallets', getUserById);
+
+module.exports = router;
\ No newline at end of file
diff --git a/server/src/validations/transaction.validation.js b/server/src/validations/transaction.validation.js
index 83239de..0e716ba 100644
--- a/server/src/validations/transaction.validation.js
+++ b/server/src/validations/transaction.validation.js
@@ -1,4 +1,4 @@
-const {z} = require('zod');
+const { z } = require('zod');
const transactionSchema = z.object({
amount: z.number().positive(),
@@ -8,4 +8,4 @@ const transactionSchema = z.object({
date: z.string().optional()
});
-module.exports = transactionSchema;
\ No newline at end of file
+module.exports = transactionSchema;
diff --git a/server/src/validations/user.validation.js b/server/src/validations/user.validation.js
new file mode 100644
index 0000000..592416e
--- /dev/null
+++ b/server/src/validations/user.validation.js
@@ -0,0 +1,16 @@
+const {z} = require('zod');
+
+const userSchema = a.onject({
+ // username: z.string().min(1).max(255),
+ // password: z.string().min(1).max(255),
+ email: z.string().email().min(1).max(255),
+ fullname: z.string().min(1).max(255),
+ numberOfWallets: z.number().positive().optional(),
+});
+
+const userLoginSchema = z.object({
+ username: z.string().min(1).max(255),
+ password: z.string().min(1).max(255),
+});
+
+module.exports = {userSchema, userLoginSchema};
\ No newline at end of file
diff --git a/server/src/validations/wallet.validation.js b/server/src/validations/wallet.validation.js
index 203ee6d..5250c88 100644
--- a/server/src/validations/wallet.validation.js
+++ b/server/src/validations/wallet.validation.js
@@ -7,6 +7,7 @@ const z = require('zod');
const walletSchema = z.object({
name: z.string().min(3).max(255),
description: z.string().min(3).max(255).optional(),
+ createdAt: z.string().optional(),
});
module.exports = walletSchema;
\ No newline at end of file