From 57abe0bb4cf58c0885610f8d98101365f6efd418 Mon Sep 17 00:00:00 2001 From: William Lewis Date: Thu, 21 Dec 2023 10:07:24 -0600 Subject: [PATCH 1/9] Added transactions list page. --- active-allocator/ClientApp/src/AppRoutes.js | 20 ++- .../ClientApp/src/components/NavMenu.js | 3 + .../ClientApp/src/components/auth/Login.js | 1 - .../components/transactions/Transactions.js | 121 ++++++++++++++++++ .../Controllers/TransactionController.cs | 2 +- .../Services/TransactionService.cs | 3 + active-allocator/appsettings.json | 2 +- 7 files changed, 148 insertions(+), 4 deletions(-) create mode 100644 active-allocator/ClientApp/src/components/transactions/Transactions.js diff --git a/active-allocator/ClientApp/src/AppRoutes.js b/active-allocator/ClientApp/src/AppRoutes.js index da40c87..481b131 100644 --- a/active-allocator/ClientApp/src/AppRoutes.js +++ b/active-allocator/ClientApp/src/AppRoutes.js @@ -5,7 +5,7 @@ import CreateAccount from "./components/accounts/CreateAccount"; import DeleteAccount from "./components/accounts/DeleteAccount"; import UpdateAccount from "./components/accounts/UpdateAccount"; import Accounts from "./components/accounts/Accounts"; -import { Login } from "./components/auth/Login"; +import Login from "./components/auth/Login"; import { Logout } from "./components/auth/Logout"; import Register from "./components/auth/Register"; import CreateUser from "./components/users/CreateUser"; @@ -13,6 +13,8 @@ import DeleteUser from "./components/users/DeleteUser"; import UpdateUser from "./components/users/UpdateUser"; import Users from "./components/users/Users"; +import Transactions from "./components/transactions/Transactions"; + const AppRoutes = [ { index: true, @@ -74,6 +76,22 @@ const AppRoutes = [ path: '/admin/account/delete', element: }, + { + path: '/admin/transactions', + element: + }, + { + path: '/admin/transaction/create', + element: + }, + { + path: '/admin/transaction/edit', + element: + }, + { + path: '/admin/transaction/delete', + element: + }, ]; export default AppRoutes; diff --git a/active-allocator/ClientApp/src/components/NavMenu.js b/active-allocator/ClientApp/src/components/NavMenu.js index 3f6774e..4fbe316 100644 --- a/active-allocator/ClientApp/src/components/NavMenu.js +++ b/active-allocator/ClientApp/src/components/NavMenu.js @@ -51,6 +51,9 @@ export class NavMenu extends Component { Accounts + + Transactions + {/* {sessionStorage.getItem('firstName')} */} diff --git a/active-allocator/ClientApp/src/components/auth/Login.js b/active-allocator/ClientApp/src/components/auth/Login.js index 0d180ac..f480195 100644 --- a/active-allocator/ClientApp/src/components/auth/Login.js +++ b/active-allocator/ClientApp/src/components/auth/Login.js @@ -4,7 +4,6 @@ import { useState } from 'react'; import { postDataForLogin } from "../services/AccessAPI"; import SessionManager from "./SessionManager"; - export default function Login() { useEffect(() => { diff --git a/active-allocator/ClientApp/src/components/transactions/Transactions.js b/active-allocator/ClientApp/src/components/transactions/Transactions.js new file mode 100644 index 0000000..7ba54e2 --- /dev/null +++ b/active-allocator/ClientApp/src/components/transactions/Transactions.js @@ -0,0 +1,121 @@ +import { getData } from "../services/AccessAPI"; +import { useNavigate, useLocation } from "react-router-dom"; +import { useEffect } from "react"; +import { useState } from 'react'; + +export default function Transactions() { + useEffect(() => { + getAllTransactionsData(); + }, []) + + let navigate = useNavigate(); + const search = useLocation().search; + const accountId = new URLSearchParams(search).get('accountId') + + const [state, setState] = useState({ + transactions: [], + loading: false + }); + + function onTransactionCreate() { + navigate('/admin/transaction/create'); + } + + function onTransactionEdit(id){ + let path = "/admin/transaction/edit" + let query = "?id=" + id + navigate(path + query); + } + + function onTransactionDelete(id){ + let path = "/admin/transaction/delete" + let query = "?id=" + id + navigate(path + query); + } + + function getAllTransactionsData() { + let url = 'Transactions/'; + if (accountId) { + url += '?accountId=' + accountId; + } + + getData(url).then( + (result) => { + if (result) { + console.log(result); + setState({ + transactions: result, + loading: false + }); + } + } + ); + } + + function renderAllTransactionsTable(transactions) { + return ( + + + + + + + + + + + + + + { + transactions.map(transaction => ( + + + + + + + + + + + )) + } + +
AmountDescriptionDateExternal IDDebit AccCredit AccPending
{formatAmount( + transaction.amount, + transaction.currencyType, + accountId ? accountId === transaction.debitAccountId : null + )}{transaction.description}{transaction.date.split("T")[0]}{transaction.externalId}{transaction.debitAccountId}{transaction.debitAccountId}{transaction.isPending ? "True" : "False"} || +
+ ); + } + + function formatAmount(amount, currencyType, isDebit = null) + { + let str = currencyType.symbol + amount + + if (isDebit != null) { + str = (isDebit ? "-" : "+") + str; + } + + return str; + } + + let content = state.loading ? ( +

+ Loading... +

+ ) : ( + renderAllTransactionsTable(state.transactions) + ) + + + return ( +
+

List of Transactions

+ + {content} +
+ ); +} \ No newline at end of file diff --git a/active-allocator/Controllers/TransactionController.cs b/active-allocator/Controllers/TransactionController.cs index e7fc922..66f6a94 100644 --- a/active-allocator/Controllers/TransactionController.cs +++ b/active-allocator/Controllers/TransactionController.cs @@ -30,7 +30,7 @@ public class TransactionsController : ControllerBase } [HttpGet] - public IActionResult GetAll() + public IActionResult GetAll(int? accountId = null) { List transactionDtos = new List(); diff --git a/active-allocator/Services/TransactionService.cs b/active-allocator/Services/TransactionService.cs index 8e56fc4..1d796ea 100644 --- a/active-allocator/Services/TransactionService.cs +++ b/active-allocator/Services/TransactionService.cs @@ -64,6 +64,9 @@ public class TransactionService : ITransactionService IsPending = model.IsPending, }; + if (transaction.DebitAccount.Id == transaction.CreditAccount.Id) + throw new AppException("The debit and credit accounts of a transaction cannot be the same."); + // Transaction Duplication Check if (transaction.ExternalId != null && _context.Transactions.Any(x => x.ExternalId == transaction.ExternalId)) throw new AppException("Transaction with the external ID '" + transaction.ExternalId + "' already exists"); diff --git a/active-allocator/appsettings.json b/active-allocator/appsettings.json index 8cce064..f1ef1b5 100644 --- a/active-allocator/appsettings.json +++ b/active-allocator/appsettings.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "PSQLConnection": "Server=localhost;Port=15432;Database=AADB;User Id=postgres;Password=nqA3UV3CliLLHpLL" + "PSQLConnection": "Server=localhost;Port=15432;Database=aadb;User Id=postgres;Password=nqA3UV3CliLLHpLL" }, "Logging": { "LogLevel": { From e77b9c1bc142bfe7ea70fdf0589de52c190f07d1 Mon Sep 17 00:00:00 2001 From: William Lewis Date: Thu, 21 Dec 2023 15:08:08 -0600 Subject: [PATCH 2/9] Added constants in place of URL strings. Fixed CreateAccount page --- active-allocator/ClientApp/src/AppRoutes.js | 37 +++++----- .../ClientApp/src/components/NavMenu.js | 17 ++--- .../src/components/accounts/Accounts.js | 19 +++-- .../src/components/accounts/CreateAccount.js | 69 ++++++------------- .../src/components/accounts/DeleteAccount.js | 5 +- .../src/components/accounts/UpdateAccount.js | 9 +-- .../ClientApp/src/components/auth/Login.js | 9 +-- .../ClientApp/src/components/auth/Logout.js | 3 +- .../ClientApp/src/components/auth/Register.js | 7 +- .../src/components/auth/SessionManager.js | 6 ++ .../src/components/services/AccessAPI.js | 8 +++ .../src/components/services/Routes.js | 20 ++++++ .../components/transactions/Transactions.js | 9 ++- .../src/components/users/CreateUser.js | 11 +-- .../src/components/users/DeleteUser.js | 11 +-- .../src/components/users/UpdateUser.js | 11 +-- .../ClientApp/src/components/users/Users.js | 13 ++-- 17 files changed, 139 insertions(+), 125 deletions(-) create mode 100644 active-allocator/ClientApp/src/components/services/Routes.js diff --git a/active-allocator/ClientApp/src/AppRoutes.js b/active-allocator/ClientApp/src/AppRoutes.js index 481b131..e0aaaec 100644 --- a/active-allocator/ClientApp/src/AppRoutes.js +++ b/active-allocator/ClientApp/src/AppRoutes.js @@ -12,6 +12,7 @@ import CreateUser from "./components/users/CreateUser"; import DeleteUser from "./components/users/DeleteUser"; import UpdateUser from "./components/users/UpdateUser"; import Users from "./components/users/Users"; +import { Routes } from "./components/services/Routes"; import Transactions from "./components/transactions/Transactions"; @@ -21,75 +22,75 @@ const AppRoutes = [ element: }, { - path: '/home', + path: Routes.HOME, element: }, { - path: '/counter', + path: Routes.COUNTER, element: }, { - path: '/fetch-data', + path: Routes.FETCH_DATA, element: }, { - path: '/login', + path: Routes.LOGIN, element: }, { - path: '/logout', + path: Routes.LOGOUT, element: }, { - path: '/register', + path: Routes.REGISTER, element: }, { - path: '/admin/users', + path: Routes.USERS, element: }, { - path: '/admin/user/create', + path: Routes.USER_CREATE, element: }, { - path: '/admin/user/edit', + path: Routes.USER_EDIT, element: }, { - path: '/admin/user/delete', + path: Routes.USER_DELETE, element: }, { - path: '/admin/accounts', + path: Routes.ACCOUNTS, element: }, { - path: '/admin/account/create', + path: Routes.ACCOUNT_CREATE, element: }, { - path: '/admin/account/edit', + path: Routes.ACCOUNT_EDIT, element: }, { - path: '/admin/account/delete', + path: Routes.ACCOUNT_DELETE, element: }, { - path: '/admin/transactions', + path: Routes.TRANSACTIONS, element: }, { - path: '/admin/transaction/create', + path: Routes.TRANSACTION_CREATE, element: }, { - path: '/admin/transaction/edit', + path: Routes.TRANSACTION_EDIT, element: }, { - path: '/admin/transaction/delete', + path: Routes.TRANSACTION_DELETE, element: }, ]; diff --git a/active-allocator/ClientApp/src/components/NavMenu.js b/active-allocator/ClientApp/src/components/NavMenu.js index 4fbe316..9b30f33 100644 --- a/active-allocator/ClientApp/src/components/NavMenu.js +++ b/active-allocator/ClientApp/src/components/NavMenu.js @@ -1,6 +1,7 @@ import React, { Component } from 'react'; import { Collapse, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap'; import { Link } from 'react-router-dom'; +import { Routes } from "./services/Routes"; import './NavMenu.css'; export class NavMenu extends Component { @@ -33,32 +34,32 @@ export class NavMenu extends Component { Home - Counter + Counter - Fetch data + Fetch data {sessionStorage.getItem('userName') == null && - Login + Login } {sessionStorage.getItem('userName') != null && <> - Users + Users - Accounts + Accounts - Transactions + Transactions {/* - {sessionStorage.getItem('firstName')} + {sessionStorage.getItem('firstName')} */} - Logout + Logout } diff --git a/active-allocator/ClientApp/src/components/accounts/Accounts.js b/active-allocator/ClientApp/src/components/accounts/Accounts.js index 441c078..fdd1354 100644 --- a/active-allocator/ClientApp/src/components/accounts/Accounts.js +++ b/active-allocator/ClientApp/src/components/accounts/Accounts.js @@ -1,7 +1,8 @@ -import { getData } from "../services/AccessAPI"; -import { useNavigate } from "react-router-dom"; +import { EndPoints, getData } from "../services/AccessAPI"; +import { RouterProvider, useNavigate } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; +import { Routes } from "../services/Routes"; export default function Accounts() { useEffect(() => { @@ -16,23 +17,21 @@ export default function Accounts() { }); function onAccountCreate() { - navigate('/admin/account/create'); + navigate(Routes.ACCOUNT_CREATE); } - function onAccountEdit(id){ - let path = "/admin/account/edit" + function onAccountEdit(id) { let query = "?id=" + id - navigate(path + query); + navigate(Routes.ACCOUNT_EDIT + query); } - function onAccountDelete(id){ - let path = "/admin/account/delete" + function onAccountDelete(id) { let query = "?id=" + id - navigate(path + query); + navigate(Routes.ACCOUNT_DELETE + query); } function getAllAccountsData() { - getData('Accounts/').then( + getData(EndPoints.ACCOUNTS).then( (result) => { if (result) { console.log(result); diff --git a/active-allocator/ClientApp/src/components/accounts/CreateAccount.js b/active-allocator/ClientApp/src/components/accounts/CreateAccount.js index 33c49a4..8694427 100644 --- a/active-allocator/ClientApp/src/components/accounts/CreateAccount.js +++ b/active-allocator/ClientApp/src/components/accounts/CreateAccount.js @@ -1,42 +1,35 @@ import SessionManager from "../auth/SessionManager"; -import { postData } from "../services/AccessAPI"; +import { EndPoints, postData } from "../services/AccessAPI"; import { useNavigate } from "react-router-dom"; import { useState } from 'react'; +import { Routes } from "../services/Routes"; export default function CreateAccount() { let navigate = useNavigate(); - const [firstName, setFirstName] = useState(""); - const [lastName, setLastName] = useState(""); - const [email, setEmail] = useState(""); - const [accountname, setAccountname] = useState(""); - const [password, setPassword] = useState(""); - const [confirmationPassword, setConfirmationPassword] = useState(""); - const [role, setRole] = useState(""); + const [name, setName] = useState(""); + const [owner, setOwner] = useState(0); + const [initBalance, setInitBalance] = useState(""); + const [currency, setCurrency] = useState(0); + const [extNum, setExtNum] = useState(""); const [loading, setLoading] = useState(true); function onSubmit(e) { e.preventDefault(); - if (password !== confirmationPassword) { - alert("Password and confirm password are not same"); - return; - } - let accountObj = { - firstName: firstName, - lastName: lastName, - email: email, - accountname: accountname, - password: password, - role: role, + name: name, + owner: SessionManager.getUserId(), + initialBalance: initBalance, + currency: 1, + externalAccountNumber: extNum, } - postData('Accounts/register', accountObj).then((result) => { + postData(EndPoints.ACCOUNTS, accountObj).then((result) => { setLoading(false); let responseJson = result; if (responseJson) { - navigate('/admin/accounts'); + navigate(Routes.ACCOUNTS); } }); } @@ -45,9 +38,9 @@ export default function CreateAccount() { e.preventDefault(); if(SessionManager.getToken()){ - navigate('/admin/accounts'); + navigate(Routes.ACCOUNTS); }else{ - navigate('/login'); + navigate(Routes.LOGIN); } } @@ -60,39 +53,19 @@ export default function CreateAccount() {

Create new account

-
- - setFirstName(e.target.value)}> -
- -
- - setLastName(e.target.value)}> -
- -
- - setEmail(e.target.value)}> -
-
- setAccountname(e.target.value)}> + setName(e.target.value)}>
- - setRole(e.target.value)}> + + setInitBalance(e.target.value)}>
- - setPassword(e.target.value)}> -
- -
- - setConfirmationPassword(e.target.value)}> + + setExtNum(e.target.value)}>
diff --git a/active-allocator/ClientApp/src/components/accounts/DeleteAccount.js b/active-allocator/ClientApp/src/components/accounts/DeleteAccount.js index 5de0c3e..c0a11c7 100644 --- a/active-allocator/ClientApp/src/components/accounts/DeleteAccount.js +++ b/active-allocator/ClientApp/src/components/accounts/DeleteAccount.js @@ -3,6 +3,7 @@ import { deleteData, getData } from "../services/AccessAPI"; import { useEffect } from "react"; import { useState } from 'react'; import { useLocation } from 'react-router-dom'; +import { Routes } from "../services/Routes"; export default function DeleteAccount() { const search = useLocation().search; @@ -41,7 +42,7 @@ export default function DeleteAccount() { }); function onCancel() { - navigate('/admin/accounts'); + navigate(Routes.ACCOUNTS); } function onConfirmation(e) { @@ -50,7 +51,7 @@ export default function DeleteAccount() { deleteData('Accounts/' + id).then((result) => { let responseJson = result; if (responseJson) { - navigate('/admin/accounts'); + navigate(Routes.ACCOUNTS); } } ); diff --git a/active-allocator/ClientApp/src/components/accounts/UpdateAccount.js b/active-allocator/ClientApp/src/components/accounts/UpdateAccount.js index 1a01988..fa00af2 100644 --- a/active-allocator/ClientApp/src/components/accounts/UpdateAccount.js +++ b/active-allocator/ClientApp/src/components/accounts/UpdateAccount.js @@ -2,6 +2,7 @@ import { getData, putData } from "../services/AccessAPI"; import { useNavigate, useLocation } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; +import { Routes } from "../services/Routes"; export default function UpdateAccount() { let navigate = useNavigate(); @@ -43,10 +44,10 @@ export default function UpdateAccount() { } function onUpdateCancel() { - navigate('/admin/accounts'); + navigate(Routes.ACCOUNTS); } - function onSubmit(e){ + function onSubmit(e) { e.preventDefault(); //const id = location.pathName.slice(location.pathName.lastIndexOf("/") , location.pathName.length); @@ -66,9 +67,9 @@ export default function UpdateAccount() { let responseJson = result; console.log("update response: "); - if(responseJson){ + if (responseJson) { console.log(responseJson); - navigate('/admin/accounts'); + navigate(Routes.ACCOUNTS); } } ); diff --git a/active-allocator/ClientApp/src/components/auth/Login.js b/active-allocator/ClientApp/src/components/auth/Login.js index f480195..47c6825 100644 --- a/active-allocator/ClientApp/src/components/auth/Login.js +++ b/active-allocator/ClientApp/src/components/auth/Login.js @@ -1,8 +1,9 @@ import { useNavigate } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; -import { postDataForLogin } from "../services/AccessAPI"; +import { EndPoints, postDataForLogin } from "../services/AccessAPI"; import SessionManager from "./SessionManager"; +import { Routes } from "../services/Routes"; export default function Login() { @@ -32,14 +33,14 @@ export default function Login() { } //console.log("login info: " + userInfo.password); - postDataForLogin('Users/authenticate', userObj).then((result) => { + postDataForLogin(EndPoints.USER_AUTHENTICATE, userObj).then((result) => { if (result?.token) { SessionManager.setUserSession(result.username, result.token, result.id, result.firstName, result.lastName) if (SessionManager.getToken()) { setLoading(false); - navigate('/home'); + navigate(Routes.HOME); } } @@ -69,7 +70,7 @@ export default function Login() { } function registration() { - navigate('/register'); + navigate(Routes.REGISTER); } diff --git a/active-allocator/ClientApp/src/components/auth/Logout.js b/active-allocator/ClientApp/src/components/auth/Logout.js index 11fa312..588d510 100644 --- a/active-allocator/ClientApp/src/components/auth/Logout.js +++ b/active-allocator/ClientApp/src/components/auth/Logout.js @@ -1,5 +1,6 @@ import { Component } from "react"; import SessionManager from "./SessionManager"; +import { Routes } from "../services/Routes"; export class Logout extends Component{ constructor(){ @@ -12,7 +13,7 @@ export class Logout extends Component{ componentDidMount(){ console.log("component did mount for logout"); SessionManager.removeUserSession(); - window.location.href = "/login"; + window.location.href = Routes.LOGIN; } render(){ diff --git a/active-allocator/ClientApp/src/components/auth/Register.js b/active-allocator/ClientApp/src/components/auth/Register.js index b23901d..5400e57 100644 --- a/active-allocator/ClientApp/src/components/auth/Register.js +++ b/active-allocator/ClientApp/src/components/auth/Register.js @@ -1,7 +1,8 @@ import { useNavigate } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; -import { postData } from "../services/AccessAPI"; +import { EndPoints, postData } from "../services/AccessAPI"; +import { Routes } from "../services/Routes"; export default function Register() { @@ -44,9 +45,9 @@ export default function Register() { role: role, } - postData('Users/register', userObj).then((result) => { + postData(EndPoints.USER_REGISTER, userObj).then((result) => { if (result) { - navigate('/login'); + navigate(Routes.LOGIN); } else { let errors = ''; for (const key in result?.errors) { diff --git a/active-allocator/ClientApp/src/components/auth/SessionManager.js b/active-allocator/ClientApp/src/components/auth/SessionManager.js index 668d5a4..a60915a 100644 --- a/active-allocator/ClientApp/src/components/auth/SessionManager.js +++ b/active-allocator/ClientApp/src/components/auth/SessionManager.js @@ -6,6 +6,12 @@ const SessionManager = { else return null; }, + getUserId() { + const id = sessionStorage.getItem('userId'); + if (id) return id; + else return null; + }, + setUserSession(userName, token, userId, firstName, lastName) { sessionStorage.setItem('userName', userName); sessionStorage.setItem('token', token); diff --git a/active-allocator/ClientApp/src/components/services/AccessAPI.js b/active-allocator/ClientApp/src/components/services/AccessAPI.js index 5b0a795..a291b63 100644 --- a/active-allocator/ClientApp/src/components/services/AccessAPI.js +++ b/active-allocator/ClientApp/src/components/services/AccessAPI.js @@ -1,6 +1,14 @@ import SessionManager from "../auth/SessionManager"; import { BASE_URL } from "./Settings"; +export const EndPoints = { + USERS: "Users", + USER_AUTHENTICATE: "Users/authenticate", + USER_REGISTER: "Users/register", + ACCOUNTS: "Accounts", + CURRENCYTYPES: "CurrencyTypes", + TRANSACTIONS: "Transactions", +} export function getData(endPoint) { diff --git a/active-allocator/ClientApp/src/components/services/Routes.js b/active-allocator/ClientApp/src/components/services/Routes.js new file mode 100644 index 0000000..22fcc04 --- /dev/null +++ b/active-allocator/ClientApp/src/components/services/Routes.js @@ -0,0 +1,20 @@ +export const Routes = { + HOME: "/home", + COUNTER: "/counter", + FETCH_DATA: "/fetch-data", + LOGIN: "/login", + LOGOUT: "/logout", + REGISTER: "/register", + USERS: "/admin/users", + USER_CREATE: "/admin/user/create", + USER_EDIT: "/admin/user/edit", + USER_DELETE: "/admin/user/delete", + ACCOUNTS: "/admin/accounts", + ACCOUNT_CREATE: "/admin/account/create", + ACCOUNT_EDIT: "/admin/account/edit", + ACCOUNT_DELETE: "/admin/account/delete", + TRANSACTIONS: "/admin/transactions", + TRANSACTION_CREATE: "/admin/transaction/create", + TRANSACTION_EDIT: "/admin/transaction/edit", + TRANSACTION_DELETE: "/admin/transaction/delete", +}; \ No newline at end of file diff --git a/active-allocator/ClientApp/src/components/transactions/Transactions.js b/active-allocator/ClientApp/src/components/transactions/Transactions.js index 7ba54e2..d0047c3 100644 --- a/active-allocator/ClientApp/src/components/transactions/Transactions.js +++ b/active-allocator/ClientApp/src/components/transactions/Transactions.js @@ -2,6 +2,7 @@ import { getData } from "../services/AccessAPI"; import { useNavigate, useLocation } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; +import { Routes } from "../services/Routes"; export default function Transactions() { useEffect(() => { @@ -18,19 +19,17 @@ export default function Transactions() { }); function onTransactionCreate() { - navigate('/admin/transaction/create'); + navigate(Routes.TRANSACTION_CREATE); } function onTransactionEdit(id){ - let path = "/admin/transaction/edit" let query = "?id=" + id - navigate(path + query); + navigate(Routes.TRANSACTION_EDIT + query); } function onTransactionDelete(id){ - let path = "/admin/transaction/delete" let query = "?id=" + id - navigate(path + query); + navigate(Routes.TRANSACTION_DELETE + query); } function getAllTransactionsData() { diff --git a/active-allocator/ClientApp/src/components/users/CreateUser.js b/active-allocator/ClientApp/src/components/users/CreateUser.js index 0e1341d..9f9554f 100644 --- a/active-allocator/ClientApp/src/components/users/CreateUser.js +++ b/active-allocator/ClientApp/src/components/users/CreateUser.js @@ -1,7 +1,8 @@ import SessionManager from "../auth/SessionManager"; -import { postData } from "../services/AccessAPI"; +import { EndPoints, postData } from "../services/AccessAPI"; import { useNavigate } from "react-router-dom"; import { useState } from 'react'; +import { Routes } from "../services/Routes"; export default function CreateUser() { let navigate = useNavigate(); @@ -32,11 +33,11 @@ export default function CreateUser() { role: role, } - postData('Users/register', userObj).then((result) => { + postData(EndPoints.USER_REGISTER, userObj).then((result) => { setLoading(false); let responseJson = result; if (responseJson) { - navigate('/admin/users'); + navigate(Routes.USERS); } }); } @@ -45,9 +46,9 @@ export default function CreateUser() { e.preventDefault(); if(SessionManager.getToken()){ - navigate('/admin/users'); + navigate(Routes.USERS); }else{ - navigate('/login'); + navigate(Routes.LOGIN); } } diff --git a/active-allocator/ClientApp/src/components/users/DeleteUser.js b/active-allocator/ClientApp/src/components/users/DeleteUser.js index f622def..9471877 100644 --- a/active-allocator/ClientApp/src/components/users/DeleteUser.js +++ b/active-allocator/ClientApp/src/components/users/DeleteUser.js @@ -1,8 +1,9 @@ import { useNavigate } from "react-router-dom"; -import { deleteData, getData } from "../services/AccessAPI"; +import { EndPoints, deleteData, getData } from "../services/AccessAPI"; import { useEffect } from "react"; import { useState } from 'react'; import { useLocation } from 'react-router-dom'; +import { Routes } from "../services/Routes"; export default function DeleteUser() { const search = useLocation().search; @@ -11,7 +12,7 @@ export default function DeleteUser() { useEffect(() => { //const { id } = this.props.match.params; - getData('Users/' + id).then( + getData(EndPoints.USERS + "/" + id).then( (result) => { console.log("Role for edit: "); console.log(result); @@ -41,16 +42,16 @@ export default function DeleteUser() { }); function onCancel() { - navigate('/admin/users'); + navigate(Routes.USERS); } function onConfirmation(e) { e.preventDefault(); - deleteData('Users/' + id).then((result) => { + deleteData(EndPoints.USERS + "/" + id).then((result) => { let responseJson = result; if (responseJson) { - navigate('/admin/users'); + navigate(Routes.USERS); } } ); diff --git a/active-allocator/ClientApp/src/components/users/UpdateUser.js b/active-allocator/ClientApp/src/components/users/UpdateUser.js index 8e1133d..26dbb14 100644 --- a/active-allocator/ClientApp/src/components/users/UpdateUser.js +++ b/active-allocator/ClientApp/src/components/users/UpdateUser.js @@ -1,7 +1,8 @@ -import { getData, putData } from "../services/AccessAPI"; +import { getData, putData, EndPoints } from "../services/AccessAPI"; import { useNavigate, useLocation } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; +import { Routes } from "../services/Routes"; export default function UpdateUser() { let navigate = useNavigate(); @@ -18,7 +19,7 @@ export default function UpdateUser() { useEffect(() => { //const id = location.pathName.slice(location.pathName.lastIndexOf("/") , location.pathName.length); - getData('Users/' + id).then( + getData(EndPoints.USERS + "/" + id).then( (result) => { //let responseJson = result; console.log("user for edit: "); @@ -43,7 +44,7 @@ export default function UpdateUser() { } function onUpdateCancel() { - navigate('/admin/users'); + navigate(Routes.USERS); } function onSubmit(e){ @@ -59,7 +60,7 @@ export default function UpdateUser() { Email: email, } - putData('Users/' + id, userProfile).then((result) => { + putData(EndPoints.USERS + "/" + id, userProfile).then((result) => { console.log("Put Request body: "); console.log(userProfile); @@ -68,7 +69,7 @@ export default function UpdateUser() { if(responseJson){ console.log(responseJson); - navigate('/admin/users'); + navigate(Routes.USERS); } } ); diff --git a/active-allocator/ClientApp/src/components/users/Users.js b/active-allocator/ClientApp/src/components/users/Users.js index 746265d..afbca02 100644 --- a/active-allocator/ClientApp/src/components/users/Users.js +++ b/active-allocator/ClientApp/src/components/users/Users.js @@ -1,7 +1,8 @@ -import { getData } from "../services/AccessAPI"; +import { getData, EndPoints } from "../services/AccessAPI"; import { useNavigate } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; +import { Routes } from "../services/Routes"; export default function Users() { useEffect(() => { @@ -16,23 +17,21 @@ export default function Users() { }); function onUserCreate() { - navigate('/admin/user/create'); + navigate(Routes.USER_CREATE); } function onUserEdit(id){ - let path = "/admin/user/edit" let query = "?id=" + id - navigate(path + query); + navigate(Routes.USER_EDIT + query); } function onUserDelete(id){ - let path = "/admin/user/delete" let query = "?id=" + id - navigate(path + query); + navigate(Routes.USER_DELETE + query); } function getAllUsersData() { - getData('Users/').then( + getData(EndPoints.USERS).then( (result) => { if (result) { console.log(result); From 1edebc9dda5d5d4e69d0a10646ba30944fc90937 Mon Sep 17 00:00:00 2001 From: William Lewis Date: Thu, 21 Dec 2023 15:16:28 -0600 Subject: [PATCH 3/9] Fixed DeleteAccount.js page. --- .../ClientApp/src/components/NavMenu.js | 3 +- .../src/components/accounts/DeleteAccount.js | 105 +++++++++++------- 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/active-allocator/ClientApp/src/components/NavMenu.js b/active-allocator/ClientApp/src/components/NavMenu.js index 9b30f33..34b11ef 100644 --- a/active-allocator/ClientApp/src/components/NavMenu.js +++ b/active-allocator/ClientApp/src/components/NavMenu.js @@ -33,12 +33,13 @@ export class NavMenu extends Component { Home + {/* Counter Fetch data - + */} {sessionStorage.getItem('userName') == null && Login diff --git a/active-allocator/ClientApp/src/components/accounts/DeleteAccount.js b/active-allocator/ClientApp/src/components/accounts/DeleteAccount.js index c0a11c7..be10dda 100644 --- a/active-allocator/ClientApp/src/components/accounts/DeleteAccount.js +++ b/active-allocator/ClientApp/src/components/accounts/DeleteAccount.js @@ -1,5 +1,5 @@ import { useNavigate } from "react-router-dom"; -import { deleteData, getData } from "../services/AccessAPI"; +import { EndPoints, deleteData, getData } from "../services/AccessAPI"; import { useEffect } from "react"; import { useState } from 'react'; import { useLocation } from 'react-router-dom'; @@ -10,19 +10,17 @@ export default function DeleteAccount() { const id = new URLSearchParams(search).get('id') useEffect(() => { - //const { id } = this.props.match.params; - - getData('Accounts/' + id).then( + getData(EndPoints.ACCOUNTS + "/" + id).then( (result) => { - console.log("Role for edit: "); - console.log(result); if (result) { setState({ - firstName: result.firstName, - lastName: result.lastName, - accountname: result.accountname, - email: result.email, - roles: result.roles, + name: result.name, + lastActivity: result.lastActivity, + createdOn: result.createdOn, + balance: result.balance, + initialBalance: result.initialBalance, + currencyId: result.currencyId, + externalAccountNumber: result.externalAccountNumber, loading: false }); } @@ -33,11 +31,13 @@ export default function DeleteAccount() { let navigate = useNavigate(); const [state, setState] = useState({ - firstName: '', - lastName: '', - accountname: '', - email: '', - roles: [], + name: '', + lastActivity: null, + createdOn: null, + balance: 0, + initialBalance: 0, + currencyId: 0, + externalAccountNumber: '', loading: true }); @@ -48,7 +48,7 @@ export default function DeleteAccount() { function onConfirmation(e) { e.preventDefault(); - deleteData('Accounts/' + id).then((result) => { + deleteData(EndPoints.ACCOUNTS + "/" + id).then((result) => { let responseJson = result; if (responseJson) { navigate(Routes.ACCOUNTS); @@ -57,46 +57,73 @@ export default function DeleteAccount() { ); } - + return (

::Delete account::

Are you sure you want to delete this?

Account Information

-
-
- First Name: -
-
- {state.firstName} -
-
- -
-
- Last Name: -
-
- {state.lastName} -
-
-
Account Name:
- {state.accountname} + {state.name}
- Email: + Last Activity:
- {state.email} + {state.lastActivity} +
+
+ +
+
+ Created On: +
+
+ {state.createdOn} +
+
+ +
+
+ Balance: +
+
+ {state.balance} +
+
+ +
+
+ Initial Balance: +
+
+ {state.initialBalance} +
+
+ +
+
+ Currency ID: +
+
+ {state.currencyId} +
+
+ +
+
+ External Account Number: +
+
+ {state.externalAccountNumber}
From 40fe18d89ea1c0c2ab788b76e41f5b5657da4bbe Mon Sep 17 00:00:00 2001 From: William Lewis Date: Thu, 21 Dec 2023 15:57:55 -0600 Subject: [PATCH 4/9] Fixed the account update methods, and added a balance calculator for accounts. --- .../src/components/accounts/CreateAccount.js | 8 +- .../src/components/accounts/UpdateAccount.js | 74 +++++++------------ active-allocator/Helpers/AutoMapperProfile.cs | 14 +--- .../Models/Accounts/AccountUpdateRequest.cs | 12 ++- active-allocator/Services/AccountService.cs | 58 ++++++++++++++- 5 files changed, 95 insertions(+), 71 deletions(-) diff --git a/active-allocator/ClientApp/src/components/accounts/CreateAccount.js b/active-allocator/ClientApp/src/components/accounts/CreateAccount.js index 8694427..8141a67 100644 --- a/active-allocator/ClientApp/src/components/accounts/CreateAccount.js +++ b/active-allocator/ClientApp/src/components/accounts/CreateAccount.js @@ -8,9 +8,9 @@ export default function CreateAccount() { let navigate = useNavigate(); const [name, setName] = useState(""); - const [owner, setOwner] = useState(0); + //const [owner, setOwner] = useState(0); const [initBalance, setInitBalance] = useState(""); - const [currency, setCurrency] = useState(0); + //const [currency, setCurrency] = useState(0); const [extNum, setExtNum] = useState(""); const [loading, setLoading] = useState(true); @@ -44,10 +44,6 @@ export default function CreateAccount() { } } - /*function onChange(e) { - //setState({ [e.target.name]: e.target.value }); - }*/ - return (
diff --git a/active-allocator/ClientApp/src/components/accounts/UpdateAccount.js b/active-allocator/ClientApp/src/components/accounts/UpdateAccount.js index fa00af2..7c4bfc1 100644 --- a/active-allocator/ClientApp/src/components/accounts/UpdateAccount.js +++ b/active-allocator/ClientApp/src/components/accounts/UpdateAccount.js @@ -1,4 +1,4 @@ -import { getData, putData } from "../services/AccessAPI"; +import { EndPoints, getData, putData } from "../services/AccessAPI"; import { useNavigate, useLocation } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; @@ -9,28 +9,22 @@ export default function UpdateAccount() { const search = useLocation().search; const id = new URLSearchParams(search).get('id') - const [firstName, setFirstName] = useState(""); - const [lastName, setLastName] = useState(""); - const [email, setEmail] = useState(""); - const [accountname, setAccountname] = useState(""); - const [role, setRole] = useState(""); + const [name, setName] = useState(""); + //const [owner, setOwner] = useState(0); + const [initBalance, setInitBalance] = useState(""); + //const [currency, setCurrency] = useState(0); + const [extNum, setExtNum] = useState(""); const [loading, setLoading] = useState(false); useEffect(() => { - - //const id = location.pathName.slice(location.pathName.lastIndexOf("/") , location.pathName.length); - getData('Accounts/' + id).then( + getData(EndPoints.ACCOUNTS + "/" + id).then( (result) => { - //let responseJson = result; - console.log("account for edit: "); - console.log(result); - console.log("Accountname: " + result.accountname) if (result) { - setFirstName(result.firstName); - setLastName(result.lastName); - setEmail(result.email); - setAccountname(result.accountname); - setRole(result.roles); + setName(result.name); + //setOwner(result.ownerId); + setInitBalance(result.initialBalance); + //setCurrency(result.currencyId); + setExtNum(result.externalAccountNumber); setLoading(false); } } @@ -49,23 +43,17 @@ export default function UpdateAccount() { function onSubmit(e) { e.preventDefault(); - - //const id = location.pathName.slice(location.pathName.lastIndexOf("/") , location.pathName.length); let accountProfile = { - id: id, - Accountname: accountname, - FirstName: firstName, - LastName: lastName, - Email: email, + name: name, + //owner: ownerId, + initialBalance: initBalance, + //currency: currencyId, + externalAccountNumber: extNum, } - putData('Accounts/' + id, accountProfile).then((result) => { - console.log("Put Request body: "); - console.log(accountProfile); - + putData(EndPoints.ACCOUNTS + "/" + id, accountProfile).then((result) => { let responseJson = result; - console.log("update response: "); if (responseJson) { console.log(responseJson); @@ -80,28 +68,22 @@ export default function UpdateAccount() {

Edit Account

-
- - setFirstName(e.target.value)} name="firstName" - onKeyDown={onKeyDown} > -
- -
- - setLastName(e.target.value)} name="lastName" - onKeyDown={onKeyDown} > -
-
- setAccountname(e.target.value)} name="accountname" + setName(e.target.value)} name="name" onKeyDown={onKeyDown} >
- - setEmail(e.target.value)} name="email" - onKeyDown={onKeyDown}> + + setInitBalance(e.target.value)} name="initBalance" + onKeyDown={onKeyDown} > +
+ +
+ + setExtNum(e.target.value)} name="extNum" + onKeyDown={onKeyDown} >
diff --git a/active-allocator/Helpers/AutoMapperProfile.cs b/active-allocator/Helpers/AutoMapperProfile.cs index 316607e..713b073 100644 --- a/active-allocator/Helpers/AutoMapperProfile.cs +++ b/active-allocator/Helpers/AutoMapperProfile.cs @@ -41,16 +41,10 @@ public class AutoMapperProfile : Profile // AccountUpdateRequest -> Account CreateMap() - .ForAllMembers(x => x.Condition( - (src, dest, prop) => - { - // ignore both null & empty string properties - if (prop == null) return false; - if (prop.GetType() == typeof(string) && string.IsNullOrEmpty((string)prop)) return false; - - return true; - } - )); + .ForMember( + dest => dest.OwnerId, + opt => opt.MapFrom(src => src.Owner) + ); // AccountCreateRequest -> Account CreateMap() diff --git a/active-allocator/Models/Accounts/AccountUpdateRequest.cs b/active-allocator/Models/Accounts/AccountUpdateRequest.cs index dcdd4db..104551f 100644 --- a/active-allocator/Models/Accounts/AccountUpdateRequest.cs +++ b/active-allocator/Models/Accounts/AccountUpdateRequest.cs @@ -5,11 +5,9 @@ using active_allocator.Entities; public class AccountUpdateRequest { - public string Name { get; set; } - public int OwnerId { get; set; } - public string InitialBalance { get; set; } - public int CurrencyId { get; set; } - public string ExternalAccountNumber { get; set; } - public ICollection Allocs { get; set; } - + public string Name { get; set; } = null; + public int? Owner { get; set; } = null; + public string InitialBalance { get; set; } = null; + public int? Currency { get; set; } = null; + public string ExternalAccountNumber { get; set; } = null; } \ No newline at end of file diff --git a/active-allocator/Services/AccountService.cs b/active-allocator/Services/AccountService.cs index 3f5ee03..d736458 100644 --- a/active-allocator/Services/AccountService.cs +++ b/active-allocator/Services/AccountService.cs @@ -10,6 +10,7 @@ using System.Collections.Generic; using System.Linq; using System; using Internal; +using Microsoft.EntityFrameworkCore; public interface IAccountService { @@ -87,18 +88,71 @@ public class AccountService : IAccountService public void Update(int id, AccountUpdateRequest model) { - var account = getAccount(id); + Account account = getAccount(id); // validate if (model.Name != account.Name && _context.Accounts.Any(x => x.Name == model.Name)) throw new AppException("Account with the name '" + model.Name + "' already exists"); + // Name + if (!string.IsNullOrWhiteSpace(model.Name)) + account.Name = model.Name; + + // Owner + if (model.Owner.HasValue) + { + account.OwnerId = model.Owner.Value; + account.Owner = _userService.GetById(model.Owner.Value); + } + + // Initial Balance + if (!string.IsNullOrWhiteSpace(model.InitialBalance)) + account.InitialBalance = Convert.ToDecimal(model.InitialBalance); + + // CurrencyType + if (model.Currency.HasValue) + { + account.CurrencyId = model.Currency.Value; + account.Currency = _context.CurrencyTypes.Find(model.Currency.Value); + } + + // External Account Number + if (!string.IsNullOrWhiteSpace(model.ExternalAccountNumber)) + account.ExternalAccountNumber = model.ExternalAccountNumber; + // copy model to account and save - _mapper.Map(model, account); + //_mapper.Map(model, account); + account.Balance = RecalculateBalance(id); _context.Accounts.Update(account); _context.SaveChanges(); } + //NEEDS TO BE SWITCHED TO AN EVENT HANDLER STYLE IMPLEMENTATION + public decimal RecalculateBalance(int id) + { + Account account = getAccount(id); + decimal amount = account.InitialBalance; + + List transactions = _context.Transactions + .Include(t => t.DebitAccount) + .Include(t => t.CreditAccount) + .Where(t => + (t.DebitAccount != null && t.DebitAccount.Id == id) + || (t.CreditAccount != null && t.CreditAccount.Id == id)) + .ToList(); + + foreach (Transaction t in transactions) + { + if (t.DebitAccount?.Id == id) { + amount -= t.Amount; + } else if (t.CreditAccount?.Id == id) { + amount += t.Amount; + } + } + + return amount; + } + public void Delete(int id) { var account = getAccount(id); From 7974eb868781b38ec8e771e3f7cc07dfb0f4efbf Mon Sep 17 00:00:00 2001 From: William Lewis Date: Fri, 22 Dec 2023 00:17:26 -0600 Subject: [PATCH 5/9] Added dataformatter and better tables --- .../src/components/accounts/Accounts.js | 61 +++++++++++-------- .../src/components/ui-elements/main.css | 0 .../ui-elements/table/DataFormatter.js | 55 +++++++++++++++++ .../components/ui-elements/table/RowButton.js | 10 +++ .../src/components/ui-elements/table/Table.js | 51 ++++++++++++++++ .../src/components/ui-elements/table/Tr.js | 22 +++++++ .../ClientApp/src/components/users/Users.js | 43 ++++++++++++- 7 files changed, 216 insertions(+), 26 deletions(-) create mode 100644 active-allocator/ClientApp/src/components/ui-elements/main.css create mode 100644 active-allocator/ClientApp/src/components/ui-elements/table/DataFormatter.js create mode 100644 active-allocator/ClientApp/src/components/ui-elements/table/RowButton.js create mode 100644 active-allocator/ClientApp/src/components/ui-elements/table/Table.js create mode 100644 active-allocator/ClientApp/src/components/ui-elements/table/Tr.js diff --git a/active-allocator/ClientApp/src/components/accounts/Accounts.js b/active-allocator/ClientApp/src/components/accounts/Accounts.js index fdd1354..a4d526a 100644 --- a/active-allocator/ClientApp/src/components/accounts/Accounts.js +++ b/active-allocator/ClientApp/src/components/accounts/Accounts.js @@ -3,6 +3,8 @@ import { RouterProvider, useNavigate } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; import { Routes } from "../services/Routes"; +import RowButton from "../ui-elements/table/RowButton"; +import DataFormatter, { RenderType } from "../ui-elements/table/DataFormatter"; export default function Accounts() { useEffect(() => { @@ -46,31 +48,40 @@ export default function Accounts() { function renderAllAccountsTable(accounts) { return ( - - - - - - - - - - - - { - accounts.map(account => ( - - - - - - - - )) - } - -
NameOwnerBalanceExternal Account NumberActions
{account.name}{account.owner}{account.balance}{account.externalAccountNumber} || -
+
+
+
+ + + + + + + + + + + + { + accounts.map(account => ( + + + + + + + + )) + } + +
NameOwnerBalanceExternal Account NumberActions
+ onAccountEdit(account.id)} /> + + onAccountDelete(account.id)} /> +
+
+
+
); } diff --git a/active-allocator/ClientApp/src/components/ui-elements/main.css b/active-allocator/ClientApp/src/components/ui-elements/main.css new file mode 100644 index 0000000..e69de29 diff --git a/active-allocator/ClientApp/src/components/ui-elements/table/DataFormatter.js b/active-allocator/ClientApp/src/components/ui-elements/table/DataFormatter.js new file mode 100644 index 0000000..7c7a70e --- /dev/null +++ b/active-allocator/ClientApp/src/components/ui-elements/table/DataFormatter.js @@ -0,0 +1,55 @@ +import React from 'react'; +import '../main.css'; + +export const RenderType = { + MoneyUnsigned: "money-unsigned", + MoneySigned: "money-signed", + MoneyNoColor: "money-nocolor", + MoneyNothing: "money-nothing", +} + +export default function DataFormatter({ + data, + renderType, + currencySymbol = "$" +}) { + + const isNotUndefinedOrNull = (object) => { + return !(object === undefined || object === null); + } + + function filterData(data) { + if (renderType != undefined && renderType != null) { + let num = Number(data) + let moneySign = true; + + switch (renderType) + { + case RenderType.MoneyUnsigned: + moneySign = false; + case RenderType.MoneySigned: + if (num >= 0) { + return { moneySign && "+"}{currencySymbol + (Math.round(num * 100) / 100).toFixed(2)} + } else { + return { moneySign && "-"}{currencySymbol + (Math.round(num * -100) / 100).toFixed(2)} + } + break; + case RenderType.MoneyNoColor: + if (num >= 0) { + return moneySign && "+" + currencySymbol + (Math.round(num * 100) / 100).toFixed(2) + } else { + return moneySign && "-" + currencySymbol + (Math.round(num * -100) / 100).toFixed(2) + } + case RenderType.MoneyNothing: + return currencySymbol + (Math.round(Math.abs(num) * 100) / 100).toFixed(2) + + } + } + + return data + } + + return ( + filterData(data) + ); +} \ No newline at end of file diff --git a/active-allocator/ClientApp/src/components/ui-elements/table/RowButton.js b/active-allocator/ClientApp/src/components/ui-elements/table/RowButton.js new file mode 100644 index 0000000..c53961a --- /dev/null +++ b/active-allocator/ClientApp/src/components/ui-elements/table/RowButton.js @@ -0,0 +1,10 @@ +import React from 'react'; +import '../main.css'; + +export default function RowButton({ text, onClick, hidden, className, disabled }) { + return ( + + ); +} \ No newline at end of file diff --git a/active-allocator/ClientApp/src/components/ui-elements/table/Table.js b/active-allocator/ClientApp/src/components/ui-elements/table/Table.js new file mode 100644 index 0000000..18b521c --- /dev/null +++ b/active-allocator/ClientApp/src/components/ui-elements/table/Table.js @@ -0,0 +1,51 @@ +import React from 'react'; +import '../main.css'; +//import VintageButton from '../../VintageButton'; +//import { Badge } from 'reactstrap'; +import { useNavigate } from "react-router-dom"; + +export default function Table({ + headerRender, + bodyRender, +}) { + let navigate = useNavigate(); + const routeChange = () =>{ + //let path = `/services/gallery/project?id=` + projectId; + //navigate(path); + } + + const isNotUndefinedOrNull = (object) => { + return !(object === undefined || object === null); + } + + return ( + + + { headerRender() } + + + { bodyRender() } + +
+ ); + + /* + +
+
+
+ {imageCaption}/ + {(isNotUndefinedOrNull(title) || isNotUndefinedOrNull(dateText) || isNotUndefinedOrNull(body) + || isNotUndefinedOrNull(priceText) || isNotUndefinedOrNull(buttonText)) && +
+ {isNotUndefinedOrNull(title) &&
{title}
} + {isNotUndefinedOrNull(dateText) &&

{dateText}

} + {isNotUndefinedOrNull(body) &&

{body}

} + {isNotUndefinedOrNull(priceText) &&

{priceText}

} + {isNotUndefinedOrNull(buttonText) && } +
} +
+
+
+ */ +} \ No newline at end of file diff --git a/active-allocator/ClientApp/src/components/ui-elements/table/Tr.js b/active-allocator/ClientApp/src/components/ui-elements/table/Tr.js new file mode 100644 index 0000000..33d6e17 --- /dev/null +++ b/active-allocator/ClientApp/src/components/ui-elements/table/Tr.js @@ -0,0 +1,22 @@ +import React from 'react'; +import '../main.css'; + +export default function Tr({ + keyValue, + body +}) { + + const isNotUndefinedOrNull = (object) => { + return !(object === undefined || object === null); + } + + function filterData(data) { + return data + } + + console.log("Key: " + keyValue); + + return ( + { body } + ); +} \ No newline at end of file diff --git a/active-allocator/ClientApp/src/components/users/Users.js b/active-allocator/ClientApp/src/components/users/Users.js index afbca02..677570c 100644 --- a/active-allocator/ClientApp/src/components/users/Users.js +++ b/active-allocator/ClientApp/src/components/users/Users.js @@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; import { Routes } from "../services/Routes"; +import RowButton from "../ui-elements/table/RowButton"; export default function Users() { useEffect(() => { @@ -74,12 +75,52 @@ export default function Users() { ); } + function renderBootstrapTable(users) { + return ( +
+
+
+ + + + + + + + + + + + { + users.map(user => ( + + + + + + + + )) + } + +
First NameLast NameUser NameEmailActions
{user.firstName}{user.lastName}{user.username}{user.email} + onUserEdit(user.id)} /> + + onUserDelete(user.id)} /> +
+
+
+
+ ); + } + let content = state.loading ? (

Loading...

) : ( - renderAllUsersTable(state.users) + //renderAllUsersTable(state.users) + renderBootstrapTable(state.users) ) From 46bdab07b34b13191590bf6aca54160a47cd8498 Mon Sep 17 00:00:00 2001 From: William Lewis Date: Fri, 22 Dec 2023 00:17:57 -0600 Subject: [PATCH 6/9] Added reference to good UI bootstrapping tool --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index badb6b7..c8f8f45 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ Reference for adding JWT authentication is [here](https://jasonwatmore.com/net-7 ### User Interface +Use [layoutit.com](https://www.layoutit.com/build). + * View Income overview * Projected Income - Based on statistical metrics (with controls for fine-tuning sample date range) * Historical Income (with source breakdown) From 2404fd944404c6df6262b344b3c2a12f95791137 Mon Sep 17 00:00:00 2001 From: William Lewis Date: Sat, 23 Dec 2023 13:37:49 -0600 Subject: [PATCH 7/9] Fixed content loading on list pages. --- .../src/components/accounts/Accounts.js | 20 ++- .../transactions/CreateTransaction.js | 116 +++++++++++++++ .../components/transactions/Transactions.js | 135 ++++++++++-------- .../Services/TransactionService.cs | 30 ++++ 4 files changed, 236 insertions(+), 65 deletions(-) create mode 100644 active-allocator/ClientApp/src/components/transactions/CreateTransaction.js diff --git a/active-allocator/ClientApp/src/components/accounts/Accounts.js b/active-allocator/ClientApp/src/components/accounts/Accounts.js index a4d526a..2ad9f85 100644 --- a/active-allocator/ClientApp/src/components/accounts/Accounts.js +++ b/active-allocator/ClientApp/src/components/accounts/Accounts.js @@ -9,6 +9,7 @@ import DataFormatter, { RenderType } from "../ui-elements/table/DataFormatter"; export default function Accounts() { useEffect(() => { getAllAccountsData(); + getAllUsersData(); }, []) let navigate = useNavigate(); @@ -18,6 +19,8 @@ export default function Accounts() { loading: false }); + const [users, setUsers] = useState(null); + function onAccountCreate() { navigate(Routes.ACCOUNT_CREATE); } @@ -36,7 +39,6 @@ export default function Accounts() { getData(EndPoints.ACCOUNTS).then( (result) => { if (result) { - console.log(result); setState({ accounts: result, loading: false @@ -46,6 +48,20 @@ export default function Accounts() { ); } + function getAllUsersData() { + getData(EndPoints.USERS).then( + (result) => { + if (result) { + setUsers(result); + } + } + ); + } + + function findUserName(id) { + return users?.find(x => x.id)?.username; + } + function renderAllAccountsTable(accounts) { return (
@@ -66,7 +82,7 @@ export default function Accounts() { accounts.map(account => ( - + diff --git a/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js b/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js new file mode 100644 index 0000000..4873586 --- /dev/null +++ b/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js @@ -0,0 +1,116 @@ +import SessionManager from "../auth/SessionManager"; +import { EndPoints, postData } from "../services/AccessAPI"; +import { useNavigate } from "react-router-dom"; +import { useState } from 'react'; +import { useEffect } from "react"; +import { Routes } from "../services/Routes"; + +export default function CreateTransaction() { + useEffect(() => { + getAllAccountsData(); + getAllUsersData(); + }, []) + + let navigate = useNavigate(); + + const [users, setUsers] = useState(null); + const [accounts, setAccounts] = useState(null); + + const [description, setDescription] = useState(""); + //const [owner, setOwner] = useState(0); + const [initBalance, setInitBalance] = useState(""); + const [date, setDate] = useState(""); + const [debitAccount, setDebitAccount] = useState(0); + const [creditAccount, setCreditAccount] = useState(0); + const [amount, setAmount] = useState(0); + const [notes, setNotes] = useState(""); + const [currencyType, setCurrencyType] = useState(0); + const [extNum, setExtNum] = useState(""); + const [isPending, setIsPending] = useState(false); + const [loading, setLoading] = useState(true); + + function onSubmit(e) { + e.preventDefault(); + + let transactionObj = { + owner: SessionManager.getUserId(), + date: date,//"2023-12-23T19:29:12.909Z" + externalId: extNum, + description: description, + debitAccount: debitAccount, + creditAccount: creditAccount, + amount: amount, + currencyType: currencyType, + notes: notes, + isPending: isPending + } + + postData(EndPoints.ACCOUNTS, transactionObj).then((result) => { + setLoading(false); + let responseJson = result; + if (responseJson) { + navigate(Routes.ACCOUNTS); + } + }); + } + + function getAllAccountsData() { + getData(EndPoints.ACCOUNTS).then( + (result) => { + if (result) { + setAccounts(result); + setLoading(false); + } + } + ); + } + + function getAllUsersData() { + getData(EndPoints.USERS).then( + (result) => { + if (result) { + setUsers(result); + } + } + ); + } + + function onClickBack(e){ + e.preventDefault(); + + if(SessionManager.getToken()){ + navigate(Routes.ACCOUNTS); + }else{ + navigate(Routes.LOGIN); + } + } + + return ( +
+
+

Create new transaction

+ +
+ + setName(e.target.value)}> +
+ +
+ + setInitBalance(e.target.value)}> +
+ +
+ + setExtNum(e.target.value)}> +
+ +
+     + +
+ +
+
+ ); +} \ No newline at end of file diff --git a/active-allocator/ClientApp/src/components/transactions/Transactions.js b/active-allocator/ClientApp/src/components/transactions/Transactions.js index d0047c3..82b71db 100644 --- a/active-allocator/ClientApp/src/components/transactions/Transactions.js +++ b/active-allocator/ClientApp/src/components/transactions/Transactions.js @@ -1,22 +1,24 @@ -import { getData } from "../services/AccessAPI"; +import { EndPoints, getData } from "../services/AccessAPI"; import { useNavigate, useLocation } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; import { Routes } from "../services/Routes"; +import RowButton from "../ui-elements/table/RowButton"; +import DataFormatter, { RenderType } from "../ui-elements/table/DataFormatter"; export default function Transactions() { useEffect(() => { getAllTransactionsData(); + getAllAccountsData(); }, []) let navigate = useNavigate(); - const search = useLocation().search; - const accountId = new URLSearchParams(search).get('accountId') + //const search = useLocation().search; + //const accountId = new URLSearchParams(search).get('accountId') - const [state, setState] = useState({ - transactions: [], - loading: false - }); + const [loading, setLoading] = useState(true); + const [transactions, setTransactions] = useState([]); + const [accounts, setAccounts] = useState([]); function onTransactionCreate() { navigate(Routes.TRANSACTION_CREATE); @@ -33,80 +35,87 @@ export default function Transactions() { } function getAllTransactionsData() { - let url = 'Transactions/'; - if (accountId) { + //let url = EndPoints.TRANSACTIONS + '/'; + /*if (accountId) { url += '?accountId=' + accountId; - } + }*/ - getData(url).then( + getData(EndPoints.TRANSACTIONS).then( (result) => { if (result) { - console.log(result); - setState({ - transactions: result, - loading: false - }); + console.log() + setTransactions(result); + setLoading(false); } } ); } - function renderAllTransactionsTable(transactions) { - return ( - - - - - - - - - - - - - - { - transactions.map(transaction => ( - - - - - - - - - - - )) - } - -
AmountDescriptionDateExternal IDDebit AccCredit AccPending
{formatAmount( - transaction.amount, - transaction.currencyType, - accountId ? accountId === transaction.debitAccountId : null - )}{transaction.description}{transaction.date.split("T")[0]}{transaction.externalId}{transaction.debitAccountId}{transaction.debitAccountId}{transaction.isPending ? "True" : "False"} || -
+ function getAllAccountsData() { + getData(EndPoints.ACCOUNTS).then( + (result) => { + if (result) { + setAccounts(result); + setLoading(false); + } + } ); } - function formatAmount(amount, currencyType, isDebit = null) - { - let str = currencyType.symbol + amount - - if (isDebit != null) { - str = (isDebit ? "-" : "+") + str; - } - - return str; + function findAccountName(id) { + return accounts?.find(x => x.id)?.name; } - let content = state.loading ? ( + function renderAllTransactionsTable(transactions) { + return ( +
+
+
+ + + + + + + + + + + + + + { + transactions.map(transaction => ( + + + + + + + + + + + )) + } + +
AmountDescriptionDateExternal IDDebit AccCredit AccPending
+ onTransactionEdit(transaction.id)} /> + + onTransactionDelete(transaction.id)} /> +
+
+
+
+ ); + } + + let content = loading ? (

Loading...

) : ( - renderAllTransactionsTable(state.transactions) + renderAllTransactionsTable(transactions) ) diff --git a/active-allocator/Services/TransactionService.cs b/active-allocator/Services/TransactionService.cs index 1d796ea..3c5cbc6 100644 --- a/active-allocator/Services/TransactionService.cs +++ b/active-allocator/Services/TransactionService.cs @@ -10,9 +10,13 @@ using System.Collections.Generic; using System.Linq; using System; using Microsoft.EntityFrameworkCore; +using Internal; public interface ITransactionService { + event EventHandler TransactionCreated; + event EventHandler TransactionUpdated; + event EventHandler TransactionDeleted; IEnumerable GetAll(); Transaction GetById(int id); void Create(TransactionCreate model); @@ -33,6 +37,28 @@ public class TransactionService : ITransactionService _mapper = mapper; } + public event EventHandler TransactionCreated; + public event EventHandler TransactionUpdated; + public event EventHandler TransactionDeleted; + + protected virtual void OnTransactionCreated(Transaction transaction) //protected virtual method + { + TransactionCreated?.Invoke(this, transaction); + Console.WriteLine("Created!!!"); + } + + protected virtual void OnTransactionUpdated(Transaction transaction) //protected virtual method + { + TransactionUpdated?.Invoke(this, transaction); + Console.WriteLine("Updated!!!"); + } + + protected virtual void OnTransactionDeleted(Transaction transaction) //protected virtual method + { + TransactionDeleted?.Invoke(this, transaction); + Console.WriteLine("Deleted!!!"); + } + public IEnumerable GetAll() { return _context.Transactions @@ -87,6 +113,8 @@ public class TransactionService : ITransactionService _context.Transactions.Add(transaction); _context.SaveChanges(); + + OnTransactionCreated(transaction); } /*public void Update(int id, AccountUpdateRequest model) @@ -108,6 +136,8 @@ public class TransactionService : ITransactionService var transaction = getTransaction(id); _context.Transactions.Remove(transaction); _context.SaveChanges(); + + OnTransactionDeleted(transaction); } private Transaction getTransaction(int id) From 03406b8b4d88c654168dcdf50cba19f0ab728371 Mon Sep 17 00:00:00 2001 From: William Lewis Date: Sat, 23 Dec 2023 15:51:50 -0600 Subject: [PATCH 8/9] Added the Select component --- README.md | 2 + active-allocator/ClientApp/package-lock.json | 932 ++++++++++++++---- active-allocator/ClientApp/package.json | 2 + active-allocator/ClientApp/src/AppRoutes.js | 3 +- .../transactions/CreateTransaction.js | 67 +- 5 files changed, 773 insertions(+), 233 deletions(-) diff --git a/README.md b/README.md index c8f8f45..e0b1a2f 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Reference for adding JWT authentication is [here](https://jasonwatmore.com/net-7 Use [layoutit.com](https://www.layoutit.com/build). +[React Select](https://react-select.com/home#fixed-options) + * View Income overview * Projected Income - Based on statistical metrics (with controls for fine-tuning sample date range) * Historical Income (with source breakdown) diff --git a/active-allocator/ClientApp/package-lock.json b/active-allocator/ClientApp/package-lock.json index 74d5bd6..764810e 100644 --- a/active-allocator/ClientApp/package-lock.json +++ b/active-allocator/ClientApp/package-lock.json @@ -14,10 +14,12 @@ "merge": "^2.1.1", "oidc-client": "^1.11.5", "react": "^18.2.0", + "react-datepicker": "^4.25.0", "react-dom": "^18.2.0", "react-router-bootstrap": "^0.26.2", "react-router-dom": "^6.11.0", "react-scripts": "^5.0.1", + "react-select": "^5.8.0", "reactstrap": "^9.1.9", "rimraf": "^5.0.0", "web-vitals": "^3.3.1", @@ -87,11 +89,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "dependencies": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" @@ -160,11 +163,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz", - "integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "dependencies": { - "@babel/types": "^7.21.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -268,31 +271,31 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz", - "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -413,28 +416,28 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", - "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -475,12 +478,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -488,9 +491,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz", - "integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", "bin": { "parser": "bin/babel-parser.js" }, @@ -1794,32 +1797,32 @@ } }, "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz", - "integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", + "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", "dependencies": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.5", - "@babel/types": "^7.21.5", - "debug": "^4.1.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" }, "engines": { @@ -1827,12 +1830,12 @@ } }, "node_modules/@babel/types": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz", - "integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "dependencies": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2114,6 +2117,128 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz", + "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", + "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2211,6 +2336,28 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.2.tgz", + "integrity": "sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==", + "dependencies": { + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "dependencies": { + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", + "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -3211,9 +3358,9 @@ } }, "node_modules/@popperjs/core": { - "version": "2.11.7", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", - "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -3749,6 +3896,11 @@ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==" }, + "node_modules/@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + }, "node_modules/@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", @@ -3764,6 +3916,24 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, + "node_modules/@types/react": { + "version": "18.2.45", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.45.tgz", + "integrity": "sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg==", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -3777,6 +3947,11 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" + }, "node_modules/@types/semver": { "version": "7.3.13", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", @@ -3892,9 +4067,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -4046,9 +4221,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -4121,9 +4296,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -5733,9 +5908,9 @@ } }, "node_modules/crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "node_modules/crypto-random-string": { "version": "2.0.0", @@ -5827,9 +6002,9 @@ } }, "node_modules/css-loader/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -6154,6 +6329,21 @@ "node": ">=10" } }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -7729,6 +7919,11 @@ "url": "https://github.com/avajs/find-cache-dir?sponsor=1" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -8005,9 +8200,9 @@ } }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -8455,6 +8650,14 @@ "he": "bin/he" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -10664,9 +10867,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -11688,6 +11891,11 @@ "node": ">= 4.0.0" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "node_modules/merge": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", @@ -11882,9 +12090,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "funding": [ { "type": "github", @@ -12578,9 +12786,9 @@ } }, "node_modules/postcss": { - "version": "8.4.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", - "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", + "version": "8.4.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", + "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", "funding": [ { "type": "opencollective", @@ -12596,7 +12804,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -13102,9 +13310,9 @@ } }, "node_modules/postcss-loader/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -14042,6 +14250,23 @@ "node": ">=14" } }, + "node_modules/react-datepicker": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.25.0.tgz", + "integrity": "sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==", + "dependencies": { + "@popperjs/core": "^2.11.8", + "classnames": "^2.2.6", + "date-fns": "^2.30.0", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.13.0", + "react-popper": "^2.3.0" + }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -14186,6 +14411,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-onclickoutside": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz", + "integrity": "sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" + }, + "peerDependencies": { + "react": "^15.5.x || ^16.x || ^17.x || ^18.x", + "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" + } + }, "node_modules/react-popper": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", @@ -14334,9 +14572,9 @@ } }, "node_modules/react-scripts/node_modules/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -14352,6 +14590,26 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/react-select": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", + "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -15002,9 +15260,9 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "bin": { "semver": "bin/semver.js" } @@ -15584,6 +15842,11 @@ "postcss": "^8.2.15" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "node_modules/sucrase": { "version": "3.32.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", @@ -16026,9 +16289,9 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -16334,6 +16597,19 @@ "requires-port": "^1.0.0" } }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -16849,9 +17125,9 @@ } }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "engines": { "node": ">=0.10.0" } @@ -17337,11 +17613,12 @@ } }, "@babel/code-frame": { - "version": "7.21.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", - "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -17389,11 +17666,11 @@ } }, "@babel/generator": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.5.tgz", - "integrity": "sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", "requires": { - "@babel/types": "^7.21.5", + "@babel/types": "^7.23.6", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -17467,25 +17744,25 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz", - "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" }, "@babel/helper-function-name": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", - "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "requires": { - "@babel/template": "^7.20.7", - "@babel/types": "^7.21.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-member-expression-to-functions": { @@ -17573,22 +17850,22 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz", - "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==" + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==" }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { "version": "7.21.0", @@ -17617,19 +17894,19 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.21.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.8.tgz", - "integrity": "sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==" + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", + "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==" }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -18468,39 +18745,39 @@ } }, "@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz", - "integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.6.tgz", + "integrity": "sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==", "requires": { - "@babel/code-frame": "^7.21.4", - "@babel/generator": "^7.21.5", - "@babel/helper-environment-visitor": "^7.21.5", - "@babel/helper-function-name": "^7.21.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.21.5", - "@babel/types": "^7.21.5", - "debug": "^4.1.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "debug": "^4.3.1", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.21.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz", - "integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==", + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", + "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", "requires": { - "@babel/helper-string-parser": "^7.21.5", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, @@ -18635,6 +18912,111 @@ "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", "requires": {} }, + "@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "requires": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + } + } + }, + "@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "requires": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "@emotion/react": { + "version": "11.11.3", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz", + "integrity": "sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==", + "requires": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + } + }, + "@emotion/serialize": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.3.tgz", + "integrity": "sha512-iD4D6QVZFDhcbH0RAG1uVu1CwVLMWUkCvAqqlewO/rxf8+87yIBAlt4+AxMiiKPLs5hFc0owNk/sLLAOROw3cA==", + "requires": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "requires": {} + }, + "@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -18700,6 +19082,28 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.39.0.tgz", "integrity": "sha512-kf9RB0Fg7NZfap83B3QOqOGg9QmD9yBudqQXzzOtn3i4y7ZUXe5ONeW34Gwi+TxhH4mvj72R1Zc300KUMa9Bng==" }, + "@floating-ui/core": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.2.tgz", + "integrity": "sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==", + "requires": { + "@floating-ui/utils": "^0.1.3" + } + }, + "@floating-ui/dom": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", + "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", + "requires": { + "@floating-ui/core": "^1.4.2", + "@floating-ui/utils": "^0.1.3" + } + }, + "@floating-ui/utils": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", + "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" + }, "@humanwhocodes/config-array": { "version": "0.11.8", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", @@ -19415,9 +19819,9 @@ } }, "@popperjs/core": { - "version": "2.11.7", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", - "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==" + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" }, "@remix-run/router": { "version": "1.6.0", @@ -19816,6 +20220,11 @@ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.2.tgz", "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==" }, + "@types/prop-types": { + "version": "15.7.11", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + }, "@types/q": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", @@ -19831,6 +20240,24 @@ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" }, + "@types/react": { + "version": "18.2.45", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.45.tgz", + "integrity": "sha512-TtAxCNrlrBp8GoeEp1npd5g+d/OejJHFxS3OWmrPBMFaVQMSN0OFySozJio5BHxTuTeug00AVXVAjfDSfk+lUg==", + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "requires": { + "@types/react": "*" + } + }, "@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -19844,6 +20271,11 @@ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" }, + "@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==" + }, "@types/semver": { "version": "7.3.13", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", @@ -19940,9 +20372,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -20021,9 +20453,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -20073,9 +20505,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -21277,9 +21709,9 @@ } }, "crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "crypto-random-string": { "version": "2.0.0", @@ -21332,9 +21764,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -21560,6 +21992,14 @@ "whatwg-url": "^8.0.0" } }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + } + }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -22745,6 +23185,11 @@ "pkg-dir": "^4.1.0" } }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -22926,9 +23371,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -23234,6 +23679,14 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "hoopy": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz", @@ -24810,9 +25263,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -25577,6 +26030,11 @@ "fs-monkey": "^1.0.3" } }, + "memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "merge": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/merge/-/merge-2.1.1.tgz", @@ -25717,9 +26175,9 @@ "dev": true }, "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" }, "natural-compare": { "version": "1.4.0", @@ -26208,11 +26666,11 @@ } }, "postcss": { - "version": "8.4.23", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.23.tgz", - "integrity": "sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==", + "version": "8.4.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", + "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", "requires": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -26475,9 +26933,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -27081,6 +27539,19 @@ "whatwg-fetch": "^3.6.2" } }, + "react-datepicker": { + "version": "4.25.0", + "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.25.0.tgz", + "integrity": "sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==", + "requires": { + "@popperjs/core": "^2.11.8", + "classnames": "^2.2.6", + "date-fns": "^2.30.0", + "prop-types": "^15.7.2", + "react-onclickoutside": "^6.13.0", + "react-popper": "^2.3.0" + } + }, "react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -27191,6 +27662,12 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "react-onclickoutside": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz", + "integrity": "sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==", + "requires": {} + }, "react-popper": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", @@ -27294,9 +27771,9 @@ } }, "semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" } @@ -27308,6 +27785,22 @@ } } }, + "react-select": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", + "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", + "requires": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + } + }, "react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -27749,9 +28242,9 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" }, "send": { "version": "0.18.0", @@ -28208,6 +28701,11 @@ "postcss-selector-parser": "^6.0.4" } }, + "stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "sucrase": { "version": "3.32.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", @@ -28541,9 +29039,9 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "tough-cookie": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", - "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "requires": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -28761,6 +29259,12 @@ "requires-port": "^1.0.0" } }, + "use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -29130,9 +29634,9 @@ } }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" }, "workbox-background-sync": { "version": "6.5.4", diff --git a/active-allocator/ClientApp/package.json b/active-allocator/ClientApp/package.json index c15223f..674c0f6 100644 --- a/active-allocator/ClientApp/package.json +++ b/active-allocator/ClientApp/package.json @@ -9,10 +9,12 @@ "merge": "^2.1.1", "oidc-client": "^1.11.5", "react": "^18.2.0", + "react-datepicker": "^4.25.0", "react-dom": "^18.2.0", "react-router-bootstrap": "^0.26.2", "react-router-dom": "^6.11.0", "react-scripts": "^5.0.1", + "react-select": "^5.8.0", "reactstrap": "^9.1.9", "rimraf": "^5.0.0", "web-vitals": "^3.3.1", diff --git a/active-allocator/ClientApp/src/AppRoutes.js b/active-allocator/ClientApp/src/AppRoutes.js index e0aaaec..6e2c4b3 100644 --- a/active-allocator/ClientApp/src/AppRoutes.js +++ b/active-allocator/ClientApp/src/AppRoutes.js @@ -15,6 +15,7 @@ import Users from "./components/users/Users"; import { Routes } from "./components/services/Routes"; import Transactions from "./components/transactions/Transactions"; +import CreateTransaction from "./components/transactions/CreateTransaction"; const AppRoutes = [ { @@ -83,7 +84,7 @@ const AppRoutes = [ }, { path: Routes.TRANSACTION_CREATE, - element: + element: }, { path: Routes.TRANSACTION_EDIT, diff --git a/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js b/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js index 4873586..5c88e77 100644 --- a/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js +++ b/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js @@ -1,30 +1,29 @@ import SessionManager from "../auth/SessionManager"; -import { EndPoints, postData } from "../services/AccessAPI"; +import { EndPoints, postData, getData } from "../services/AccessAPI"; import { useNavigate } from "react-router-dom"; import { useState } from 'react'; import { useEffect } from "react"; import { Routes } from "../services/Routes"; +import Select from 'react-select' export default function CreateTransaction() { useEffect(() => { getAllAccountsData(); - getAllUsersData(); + //getAllUsersData(); }, []) let navigate = useNavigate(); - const [users, setUsers] = useState(null); - const [accounts, setAccounts] = useState(null); - + //const [users, setUsers] = useState(null); + const [accounts, setAccounts] = useState([]); const [description, setDescription] = useState(""); //const [owner, setOwner] = useState(0); - const [initBalance, setInitBalance] = useState(""); const [date, setDate] = useState(""); const [debitAccount, setDebitAccount] = useState(0); const [creditAccount, setCreditAccount] = useState(0); const [amount, setAmount] = useState(0); const [notes, setNotes] = useState(""); - const [currencyType, setCurrencyType] = useState(0); + //const [currencyType, setCurrencyType] = useState(0); const [extNum, setExtNum] = useState(""); const [isPending, setIsPending] = useState(false); const [loading, setLoading] = useState(true); @@ -40,12 +39,12 @@ export default function CreateTransaction() { debitAccount: debitAccount, creditAccount: creditAccount, amount: amount, - currencyType: currencyType, + currencyType: 1, notes: notes, isPending: isPending } - postData(EndPoints.ACCOUNTS, transactionObj).then((result) => { + postData(EndPoints.TRANSACTIONS, transactionObj).then((result) => { setLoading(false); let responseJson = result; if (responseJson) { @@ -58,46 +57,73 @@ export default function CreateTransaction() { getData(EndPoints.ACCOUNTS).then( (result) => { if (result) { - setAccounts(result); + setAccounts(result.map(a => { + return { + value: a.id, + label: a.name + } + })); setLoading(false); } } ); } - function getAllUsersData() { + /*function getAllUsersData() { getData(EndPoints.USERS).then( (result) => { if (result) { setUsers(result); + setLoading(false); } } ); - } + }*/ function onClickBack(e){ e.preventDefault(); if(SessionManager.getToken()){ - navigate(Routes.ACCOUNTS); + navigate(Routes.TRANSACTIONS); }else{ navigate(Routes.LOGIN); - } + } } + const options = [ + { value: 'chocolate', label: 'Chocolate' }, + { value: 'strawberry', label: 'Strawberry' }, + { value: 'vanilla', label: 'Vanilla' } + ] + /* + { + "date": "2023-12-23T21:44:31.930Z", + "isPending": true +} + */ return (

Create new transaction

- - setName(e.target.value)}> + + setDescription(e.target.value)}>
- - setInitBalance(e.target.value)}> + + setCreditAccount(e.value)} name="creditAccount" options={accounts} isSearchable="true" isLoading={loading} /> +
+ +
+ + setAmount(e.target.value)}>
@@ -105,6 +131,11 @@ export default function CreateTransaction() { setExtNum(e.target.value)}>
+
+ + setNotes(e.target.value)}> +
+
    From 4df133699ad6af9e2aec3325b311021ac26c59c6 Mon Sep 17 00:00:00 2001 From: William Lewis Date: Sat, 23 Dec 2023 23:10:16 -0600 Subject: [PATCH 9/9] Finished adding transactions. Transactions account balance updates. --- .../src/components/accounts/Accounts.js | 14 +++- .../src/components/services/AccessAPI.js | 1 + .../transactions/CreateTransaction.js | 26 ++++++- .../components/transactions/Transactions.js | 4 +- .../Controllers/AccountController.cs | 7 ++ active-allocator/Helpers/DataContext.cs | 39 +++++++++++ active-allocator/Services/AccountService.cs | 47 ++++--------- .../Services/TransactionService.cs | 70 ++++++------------- 8 files changed, 122 insertions(+), 86 deletions(-) diff --git a/active-allocator/ClientApp/src/components/accounts/Accounts.js b/active-allocator/ClientApp/src/components/accounts/Accounts.js index 2ad9f85..228479f 100644 --- a/active-allocator/ClientApp/src/components/accounts/Accounts.js +++ b/active-allocator/ClientApp/src/components/accounts/Accounts.js @@ -1,4 +1,4 @@ -import { EndPoints, getData } from "../services/AccessAPI"; +import { EndPoints, getData, putData } from "../services/AccessAPI"; import { RouterProvider, useNavigate } from "react-router-dom"; import { useEffect } from "react"; import { useState } from 'react'; @@ -58,6 +58,16 @@ export default function Accounts() { ); } + function refreshAccount(id) { + getData(EndPoints.ACCOUNTS_REFRESHBALANCE + "/" + id).then( + (result) => { + if (result) { + getAllAccountsData(); + } + } + ); + } + function findUserName(id) { return users?.find(x => x.id)?.username; } @@ -89,6 +99,8 @@ export default function Accounts() { onAccountEdit(account.id)} /> onAccountDelete(account.id)} /> + + refreshAccount(account.id)} /> )) diff --git a/active-allocator/ClientApp/src/components/services/AccessAPI.js b/active-allocator/ClientApp/src/components/services/AccessAPI.js index a291b63..a9dd1cf 100644 --- a/active-allocator/ClientApp/src/components/services/AccessAPI.js +++ b/active-allocator/ClientApp/src/components/services/AccessAPI.js @@ -6,6 +6,7 @@ export const EndPoints = { USER_AUTHENTICATE: "Users/authenticate", USER_REGISTER: "Users/register", ACCOUNTS: "Accounts", + ACCOUNTS_REFRESHBALANCE: "Accounts/RefreshBalance", CURRENCYTYPES: "CurrencyTypes", TRANSACTIONS: "Transactions", } diff --git a/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js b/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js index 5c88e77..aec39e2 100644 --- a/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js +++ b/active-allocator/ClientApp/src/components/transactions/CreateTransaction.js @@ -5,6 +5,7 @@ import { useState } from 'react'; import { useEffect } from "react"; import { Routes } from "../services/Routes"; import Select from 'react-select' +import DatePicker from 'react-datepicker' export default function CreateTransaction() { useEffect(() => { @@ -111,6 +112,11 @@ export default function CreateTransaction() { setDescription(e.target.value)}>
+
+ + setAmount(e.target.value)}> +
+
setAmount(e.target.value)}> -
+ + {/* setDate(date)} />*/} + setDate(e.target.value)}> +
@@ -135,6 +142,19 @@ export default function CreateTransaction() { setNotes(e.target.value)}>
+ +
+ +
    diff --git a/active-allocator/ClientApp/src/components/transactions/Transactions.js b/active-allocator/ClientApp/src/components/transactions/Transactions.js index 82b71db..e97c79f 100644 --- a/active-allocator/ClientApp/src/components/transactions/Transactions.js +++ b/active-allocator/ClientApp/src/components/transactions/Transactions.js @@ -63,7 +63,9 @@ export default function Transactions() { } function findAccountName(id) { - return accounts?.find(x => x.id)?.name; + let name = accounts?.find(x => x.id == id)?.name; + console.log("Id: " + id + ", Name: " + name); + return name; } function renderAllTransactionsTable(transactions) { diff --git a/active-allocator/Controllers/AccountController.cs b/active-allocator/Controllers/AccountController.cs index b5ba689..9367021 100644 --- a/active-allocator/Controllers/AccountController.cs +++ b/active-allocator/Controllers/AccountController.cs @@ -67,4 +67,11 @@ public class AccountsController : ControllerBase _accountService.Delete(id); return Ok(new { message = "account deleted" }); } + + [HttpGet("RefreshBalance/{id}")] + public IActionResult RefreshBalance(int id) + { + Account account = _accountService.RefreshAccountBalance(id); + return Ok(_mapper.Map(account)); + } } \ No newline at end of file diff --git a/active-allocator/Helpers/DataContext.cs b/active-allocator/Helpers/DataContext.cs index 074ebc7..56b69d4 100644 --- a/active-allocator/Helpers/DataContext.cs +++ b/active-allocator/Helpers/DataContext.cs @@ -3,6 +3,7 @@ namespace active_allocator.Helpers; using Microsoft.EntityFrameworkCore; using active_allocator.Entities; using System.Diagnostics; +using System; public class DataContext : DbContext { @@ -19,6 +20,44 @@ public class DataContext : DbContext options.UseInMemoryDatabase("TestDb"); }*/ + public void AddAmountToAccount(int accoundId, decimal amount) + { + Account account = this.Accounts.Find(accoundId); + if (account != null) + { + account.Balance += amount; + this.Accounts.Update(account); + this.SaveChanges(); + } + } + + public void RecalculateAccountBalance(int id) + { + Account account = this.Accounts.Find(id); + decimal amount = account.InitialBalance; + + List transactions = this.Transactions + .Include(t => t.DebitAccount) + .Include(t => t.CreditAccount) + .Where(t => + (t.DebitAccount != null && t.DebitAccount.Id == id) + || (t.CreditAccount != null && t.CreditAccount.Id == id)) + .ToList(); + + foreach (Transaction t in transactions) + { + if (t.DebitAccount?.Id == id) { + amount -= t.Amount; + } else if (t.CreditAccount?.Id == id) { + amount += t.Amount; + } + } + + account.Balance = amount; + this.Accounts.Update(account); + this.SaveChanges(); + } + public DbSet Users { get; set; } public DbSet Accounts { get; set; } public DbSet Allocs { get; set; } diff --git a/active-allocator/Services/AccountService.cs b/active-allocator/Services/AccountService.cs index d736458..723556c 100644 --- a/active-allocator/Services/AccountService.cs +++ b/active-allocator/Services/AccountService.cs @@ -15,10 +15,11 @@ using Microsoft.EntityFrameworkCore; public interface IAccountService { IEnumerable GetAll(); - Account GetById(int id); + Account GetById(int accountId); void Create(AccountCreateRequest model); - void Update(int id, AccountUpdateRequest model); - void Delete(int id); + void Update(int accountId, AccountUpdateRequest model); + Account RefreshAccountBalance(int accountId); + void Delete(int accountId); } public class AccountService : IAccountService @@ -45,9 +46,9 @@ public class AccountService : IAccountService return _context.Accounts; } - public Account GetById(int id) + public Account GetById(int accountId) { - return getAccount(id); + return getAccount(accountId); } public void Create(AccountCreateRequest model) @@ -86,9 +87,9 @@ public class AccountService : IAccountService _context.SaveChanges(); } - public void Update(int id, AccountUpdateRequest model) + public void Update(int accountId, AccountUpdateRequest model) { - Account account = getAccount(id); + Account account = getAccount(accountId); // validate if (model.Name != account.Name && _context.Accounts.Any(x => x.Name == model.Name)) @@ -122,40 +123,20 @@ public class AccountService : IAccountService // copy model to account and save //_mapper.Map(model, account); - account.Balance = RecalculateBalance(id); _context.Accounts.Update(account); _context.SaveChanges(); + _context.RecalculateAccountBalance(accountId); } - //NEEDS TO BE SWITCHED TO AN EVENT HANDLER STYLE IMPLEMENTATION - public decimal RecalculateBalance(int id) + public Account RefreshAccountBalance(int accountId) { - Account account = getAccount(id); - decimal amount = account.InitialBalance; - - List transactions = _context.Transactions - .Include(t => t.DebitAccount) - .Include(t => t.CreditAccount) - .Where(t => - (t.DebitAccount != null && t.DebitAccount.Id == id) - || (t.CreditAccount != null && t.CreditAccount.Id == id)) - .ToList(); - - foreach (Transaction t in transactions) - { - if (t.DebitAccount?.Id == id) { - amount -= t.Amount; - } else if (t.CreditAccount?.Id == id) { - amount += t.Amount; - } - } - - return amount; + _context.RecalculateAccountBalance(accountId); + return _context.Accounts.Find(accountId); } - public void Delete(int id) + public void Delete(int accountId) { - var account = getAccount(id); + var account = getAccount(accountId); _context.Accounts.Remove(account); _context.SaveChanges(); } diff --git a/active-allocator/Services/TransactionService.cs b/active-allocator/Services/TransactionService.cs index 3c5cbc6..a42f4f1 100644 --- a/active-allocator/Services/TransactionService.cs +++ b/active-allocator/Services/TransactionService.cs @@ -14,9 +14,6 @@ using Internal; public interface ITransactionService { - event EventHandler TransactionCreated; - event EventHandler TransactionUpdated; - event EventHandler TransactionDeleted; IEnumerable GetAll(); Transaction GetById(int id); void Create(TransactionCreate model); @@ -37,28 +34,6 @@ public class TransactionService : ITransactionService _mapper = mapper; } - public event EventHandler TransactionCreated; - public event EventHandler TransactionUpdated; - public event EventHandler TransactionDeleted; - - protected virtual void OnTransactionCreated(Transaction transaction) //protected virtual method - { - TransactionCreated?.Invoke(this, transaction); - Console.WriteLine("Created!!!"); - } - - protected virtual void OnTransactionUpdated(Transaction transaction) //protected virtual method - { - TransactionUpdated?.Invoke(this, transaction); - Console.WriteLine("Updated!!!"); - } - - protected virtual void OnTransactionDeleted(Transaction transaction) //protected virtual method - { - TransactionDeleted?.Invoke(this, transaction); - Console.WriteLine("Deleted!!!"); - } - public IEnumerable GetAll() { return _context.Transactions @@ -73,6 +48,20 @@ public class TransactionService : ITransactionService return getTransaction(id); } + private Account prepareAccount(int? accountId) + { + if (accountId == null || accountId.Value == 0) + { + accountId = 0; + } + + Account account = _context.Accounts.Find(accountId.Value); + if (account == null) + throw new AppException("Could not find account with ID of '" + accountId.Value + "'."); + + return account; + } + public void Create(TransactionCreate model) { Transaction transaction = new Transaction { @@ -81,9 +70,9 @@ public class TransactionService : ITransactionService Date = model.Date, CreatedOn = DateTime.UtcNow, UpdatedOn = DateTime.UtcNow, - ExternalId = model.ExternalId, - DebitAccount = model.DebitAccount == null ? null : _context.Accounts.Find(model.DebitAccount.Value), - CreditAccount = model.CreditAccount == null ? null : _context.Accounts.Find(model.CreditAccount.Value), + ExternalId = string.IsNullOrWhiteSpace(model.ExternalId) ? "" : model.ExternalId, + DebitAccount = prepareAccount(model.DebitAccount), + CreditAccount = prepareAccount(model.CreditAccount), Amount = Convert.ToDecimal(model.Amount), CurrencyType = _context.CurrencyTypes.Find(model.CurrencyType), Notes = model.Notes, @@ -94,7 +83,7 @@ public class TransactionService : ITransactionService throw new AppException("The debit and credit accounts of a transaction cannot be the same."); // Transaction Duplication Check - if (transaction.ExternalId != null && _context.Transactions.Any(x => x.ExternalId == transaction.ExternalId)) + if (!string.IsNullOrWhiteSpace(transaction.ExternalId) && _context.Transactions.Any(x => x.ExternalId == transaction.ExternalId)) throw new AppException("Transaction with the external ID '" + transaction.ExternalId + "' already exists"); if (_context.Transactions.Any(x => @@ -107,37 +96,22 @@ public class TransactionService : ITransactionService { throw new AppException("Transaction with the same fields already exists"); } - - // map model to new account object - //var account = _mapper.Map(model); _context.Transactions.Add(transaction); _context.SaveChanges(); - OnTransactionCreated(transaction); + _context.AddAmountToAccount(transaction.DebitAccount.Id, -transaction.Amount); + _context.AddAmountToAccount(transaction.CreditAccount.Id, transaction.Amount); } - /*public void Update(int id, AccountUpdateRequest model) - { - var account = getOuterTransaction(id); - - // validate - if (model.Name != account.Name && _context.Accounts.Any(x => x.Name == model.Name)) - throw new AppException("Account with the name '" + model.Name + "' already exists"); - - // copy model to account and save - _mapper.Map(model, account); - _context.Accounts.Update(account); - _context.SaveChanges(); - }*/ - public void Delete(int id) { var transaction = getTransaction(id); _context.Transactions.Remove(transaction); _context.SaveChanges(); - OnTransactionDeleted(transaction); + _context.AddAmountToAccount(transaction.DebitAccount.Id, transaction.Amount); + _context.AddAmountToAccount(transaction.CreditAccount.Id, -transaction.Amount); } private Transaction getTransaction(int id)