diff --git a/README.md b/README.md index d596703..d52709e 100644 --- a/README.md +++ b/README.md @@ -110,4 +110,39 @@ Visit the [Nx Documentation](https://nx.dev) to learn more. 1. Testing your code. Two folders are provided for you to create tests for your code, `server-e2e` and `frontend-e2e`. 2. Building tests are a great way to make sure any edits in your code do not accidentally break something elsewhere. To use run `npx nx e2e SPECIFIC-FOLDER --skip-cache`. 3. Coding some tests on the frontend require a backend, like the tests provided in `frontend-e2e` folder (tests that require CRUD functionality). This means that you must have your localhost backend running when calling this folder to test the application. NOTE: in the `tests.yml` file there is an example of how to have your backend server running while you use the testing command. - 4. To get CRUD functionality working in Github actions we need to first set up some Github secrets. In your repo go to 'Settings' -> 'Secrets and variables' -> 'Actions' and create a new secret called `FIREBASE_ADMINSDK_KEY` and then set it to the value in your `.env` file. Next we have to allow your Google Project to allow Github access. To do this create another secret and call it `GCP_PRIVATE_KEY` and set it to your Google Cloud Key. NOTE: if you do not have the key saved somewhere, then you can just create a new one, go to the google cloud console and select your project, navigate to 'IAM & Admin' -> 'Service Accounts' -> 'Keys' and create a JSON key. \ No newline at end of file + 4. To get CRUD functionality working in Github actions we need to first set up some Github secrets. In your repo go to 'Settings' -> 'Secrets and variables' -> 'Actions' and create a new secret called `FIREBASE_ADMINSDK_KEY` and then set it to the value in your `.env` file. Next we have to allow your Google Project to allow Github access. To do this create another secret and call it `GCP_PRIVATE_KEY` and set it to your Google Cloud Key. NOTE: if you do not have the key saved somewhere, then you can just create a new one, go to the google cloud console and select your project, navigate to 'IAM & Admin' -> 'Service Accounts' -> 'Keys' and create a JSON key. + + +# Getting Started for New Developers: + +To begin contributing to the project, follow these steps: + +1. Clone the main branch and switch to the "AT" branch: + - `git clone https://github.com/tirzah-dev/WOL-Planner.git` + - `git checkout AT` + +2. Create a new branch from the "AT" branch for your changes. For example: + - `git checkout -b setup` + +3. Install project dependencies: + - `npm i` + +4. Create a `.env` file at the root of the directory: + - `touch .env` + +5. Reach out to the Project Manager for the necessary content to populate the `.env` file. + +Once set up, you can start the frontend and backend servers for testing: + +- Start the backend server: + - `npx nx serve server` + +- Open another terminal and start the frontend server: + - `npx nx serve frontend` + +For more information on installing Nx into an existing repository, refer to the [Installing Nx](https://nx.dev/getting-started/installation) guide: + +- Initialize Nx in your project: + - `npx nx@latest init` + +Please note that instead of `npx nx serve server`, you can use `nx serve server` and `nx serve frontend`. \ No newline at end of file diff --git a/apps/frontend/src/app/app.module.scss b/apps/frontend/src/app/app.module.scss index 4f8c3d2..d70bca4 100644 --- a/apps/frontend/src/app/app.module.scss +++ b/apps/frontend/src/app/app.module.scss @@ -1,6 +1,13 @@ /* Your styles goes here. */ -body { - min-height: 100vh; - display: flex; - flex-direction: column; -} \ No newline at end of file +// body { +// min-height: 100vh; +// display: flex; +// flex-direction: column; +// } + +// .page { +// height: 100vh; +// display: grid; +// grid-template-rows: 40px 137px 98px 66px 116px 66px 96px 106px; +// } + diff --git a/apps/frontend/src/app/app.tsx b/apps/frontend/src/app/app.tsx index ce9dc71..41c79ec 100644 --- a/apps/frontend/src/app/app.tsx +++ b/apps/frontend/src/app/app.tsx @@ -1,51 +1,94 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { BrowserRouter } from 'react-router-dom'; import Footer from './components/footer/footer'; -// import Header from './components/header/header'; +import Header from './components/header/header'; import Router from './views/router/router'; +import { AssetProvider } from './views/assets/AssetContext'; import './app.module.scss'; +// import { UpdateAssets } from './views/assets/UpdateAssets'; +import { AssetsInput } from './views/assets/AssetsInput'; // eslint-disable-next-line @typescript-eslint/no-empty-function, @typescript-eslint/no-explicit-any -export const UserContext = React.createContext({user: {firstName: null, lastName: null, id: null, joinDate: null, email: null, userType: 'Reader', picture: null, name: null, roles: ['None']}, setUser: (user: any) => {}}); +export const UserContext = React.createContext({ + user: { + firstName: null, + lastName: null, + id: null, + joinDate: null, + email: null, + userType: 'Reader', + picture: null, + name: null, + roles: ['None'], + }, + setUser: (user: any) => {}, +}); + -export function App() { - const [user, setUser] = React.useState({firstName: null, lastName: null, id: null, joinDate: null, email: null, userType: 'Reader', picture: null, name: null, roles: ['None']}); + +export function App() { + const [user, setUser] = React.useState({ + firstName: null, + lastName: null, + id: null, + joinDate: null, + email: null, + userType: 'Reader', + picture: null, + name: null, + roles: ['None'], + }); useEffect(() => { const user = localStorage.getItem('user'); - if(user){ + if (user) { setUser(JSON.parse(user)); } - }, []) + }, []); useEffect(() => { - console.log(user) - }, [user]) + console.log(user); + }, [user]); // this is for cypress testing, to have a test user logged in useEffect(() => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - if(window.Cypress){ - const user = {firstName: 'Test', lastName: 'User', id: 'test', joinDate: '05/05/2023', email: 'testUser@test.com', userType: 'Reader', picture: 'www.google.com', name: 'Test User', roles: ['None']} - setUser(user) + if (window.Cypress) { + const user = { + firstName: 'Test', + lastName: 'User', + id: 'test', + joinDate: '05/05/2023', + email: 'testUser@test.com', + userType: 'Reader', + picture: 'www.google.com', + name: 'Test User', + roles: ['None'], + }; + setUser(user); } - }, []) + }, []); return ( - - -
-
- {/*
*/} - -
-
-
-
-
+ // + // {/* */} + // {/* */} + + +
+
+
+ + +
+ +
+
+ // {/*
*/} + //
); } -export default App; \ No newline at end of file +export default App; diff --git a/apps/frontend/src/app/components/authlayout/AuthLayout.tsx b/apps/frontend/src/app/components/authlayout/AuthLayout.tsx index a90970f..36ea910 100644 --- a/apps/frontend/src/app/components/authlayout/AuthLayout.tsx +++ b/apps/frontend/src/app/components/authlayout/AuthLayout.tsx @@ -4,21 +4,19 @@ import backgroundImage from './background-auth.jpg' export function AuthLayout({ children }: any) { return ( -
-
-
-
- {children} -
-
-
- blue background +
+
+
+ {children}
+
+ blue background +
) } \ No newline at end of file diff --git a/apps/frontend/src/app/components/hamburger-nav/HamburgerNav.tsx b/apps/frontend/src/app/components/hamburger-nav/HamburgerNav.tsx new file mode 100644 index 0000000..63a9205 --- /dev/null +++ b/apps/frontend/src/app/components/hamburger-nav/HamburgerNav.tsx @@ -0,0 +1,63 @@ +import React, { useContext } from 'react'; +import crystalBall from '../../images/dash/crystal-ball.svg'; +import lineList from '../../images/dash/line-md_list-3-filled.svg'; +import { Link } from 'react-router-dom'; + +import './hamburger.css'; + +interface HamburgerNavProps { + show: boolean; + } + +const HamburgerNav: React.FC = ({ show }) => { + return ( +
+ +
+ ); +}; +export default HamburgerNav; diff --git a/apps/frontend/src/app/components/hamburger-nav/hamburger.css b/apps/frontend/src/app/components/hamburger-nav/hamburger.css new file mode 100644 index 0000000..ee62029 --- /dev/null +++ b/apps/frontend/src/app/components/hamburger-nav/hamburger.css @@ -0,0 +1,134 @@ +.sidebar { + position: fixed; + top: 0; + left: -50%; + width: 50%; + height: 100vh; + background-color: #15171c; + transition: left 0.5s ease-in-out; +} + +.sidebar.show { + left: 0; +} + +.nav-container { + position: absolute; + top: 0; + left: 0; + width: 50%; + height: 100%; + background-color: black; + transition: transform 1s ease; +} + +.showSideBar { + transform: translateX(0); +} + +.hideSideBar { + transform: translateX(-250px); +} + +.opaque-container { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + background-color: #333333; + opacity: 0.5; +} + +.fly-out-menu-root { + background-color: black; + position: absolute; + left: 0; + top: 0; + display: flex; + flex-direction: column; + gap: 5%; + width: 100%; + height: 100%; +} + +.ham-trademark-container { + padding-top: 25%; + padding-bottom: 15%; + display: flex; + align-items: center; + width: 100%; + justify-content: center; + gap: 1rem; + height: 10rem; +} + +.ham-trademark-letter { + font-size: 1.5rem; + color: white; + padding: 0px; + margin: 0px; + bottom: 0px; +} + +.ham-trademark-symbol { + width: 1.55rem; + height: 1.55rem; + padding: 0px; + margin: 0px; + +} + +.ham-vertical-line { + position: relative; + display: flex; + +} + +.ham-vertical-line::before { + content: ''; + position: absolute; + top: 0rem; + bottom: 0rem; + left: 0.02rem; + width: 1px; + background-color: rgb(255, 255, 255); +} + +.ham-crystalball { + width: 1.95rem; + height: 1.95rem; + padding: 0px; + margin: 0px; + + padding-left: 0.5rem; +} + +.link-container { + position: relative; + display: flex; + flex-direction: column; + width: 100%; + font-family: 'Sofia_Pro', sans-serif; + align-items: center; + justify-content: center; +} + +.hamburger-link { + font-weight: bold; + color: white; + border-top: 1px solid #6F6F6F; + width: 100%; + height: 2.5rem; + display: flex; + align-items: center; + justify-content: center; +} + +.hamburger-link:hover { + text-decoration: underline; +} + +.profile-image { + margin-right: 5%; +} + diff --git a/apps/frontend/src/app/components/header/header.css b/apps/frontend/src/app/components/header/header.css new file mode 100644 index 0000000..631155a --- /dev/null +++ b/apps/frontend/src/app/components/header/header.css @@ -0,0 +1,185 @@ +.SidebarNav { + background: #15171c; + width: 250px; + height: 100vh; + display: flex; + justify-content: center; + position: fixed; + top: 0; + left: 0; + transition: 650ms; + z-index: 999; + color: black; +} + +.HideSidebarNav { + background: #15171c; + width: 250px; + height: 100vh; + display: flex; + justify-content: center; + position: fixed; + top: 0; + left: -100%; + transition: 650ms; + z-index: 10; +} + +.SidebarWrap { + width: 100%; +} + +.header-container { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + flex-basis: auto; +} + +.header-sub-container { + justify-content: space-around; + margin-left: 1.75rem; + width: 100%; + align-items: center; + width: 100%; + flex-basis: auto; + flex-grow: 1; +} + + +.trademark-image, +.trademark-letter { + font-size: large; + color: white; +} + +.trademark-letter { + margin-top: 0.25rem; + padding-left: 1rem; + padding-right: 1rem; +} + +.vertical-line { + position: relative; + display: flex; +} + +.vertical-line::before { + content: ' '; + position: absolute; + top: 0.5rem; + bottom: 0.25rem; + left: 0.02rem; + width: 1px; + background-color: rgb(255, 255, 255); +} + +.head-trademark-lineList { + margin-top: 0.40rem; + width: 1.25rem; +} + +.navLink-list { + display: flex; + justify-content: space-around; +} + + +.head-trademark-container { + padding-top: 25%; + padding-bottom: 15%; + display: flex; + align-items: center; + width: 100%; + justify-content: center; + gap: 1rem; + height: 10rem; +} + +.head-trademark-letter { + font-size: 1.5rem; + color: white; + padding: 0; + margin: 0; + bottom: 0; +} + +.head-trademark-symbol { + width: 1.55rem; + height: 1.55rem; + padding: 0; + margin: 0; +} + +.head-vertical-line { + position: relative; + display: flex; +} + +.head-vertical-line::before { + content: ''; + position: absolute; + top: 0.35rem; + bottom: 0.25rem; + left: 0.02rem; + width: 1px; + background-color: rgb(255, 255, 255); +} + +.head-crystalball { + width: 1.95rem; + height: 1.95rem; + padding: 0; + margin: 0; + padding-left: 0.5rem; +} + +.unordered-list { + justify-content: space-around; + width: 100%; + display: flex; + flex-direction: row; + margin-right: auto; + padding: 0; +} + +.li-navlink { + color: white; + font-weight: bold; + margin-bottom: 0; + padding-right: 0.5rem; +} + +.li-navlink:hover { + text-decoration: underline; +} + +.header-first-time-user-text { + font-family: 'Sofia Pro', sans-serif; + font-size: 1rem; + font-weight: 800; + letter-spacing: 0px; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + border-width: 3px; + padding: 1rem 2.5rem 1rem 2.5rem; + height: 2rem; + border-color: white; + border-radius: 30px; + color: white; +} + +.header-first-time-user-text:hover { + text-decoration: underline; +} + + + +.header-profile-image { + height: 2rem; + width: 2rem; + border-radius: 10rem; +} diff --git a/apps/frontend/src/app/components/header/header.tsx b/apps/frontend/src/app/components/header/header.tsx index 783b7c4..973a106 100644 --- a/apps/frontend/src/app/components/header/header.tsx +++ b/apps/frontend/src/app/components/header/header.tsx @@ -1,293 +1,172 @@ import React, { useEffect, useRef, useState } from 'react'; -import { NavLink } from 'react-router-dom'; +import { NavLink, Navigate } from 'react-router-dom'; import { UserContext } from '../../app'; -import { Logo } from './Logo'; -import { Transition } from '@headlessui/react'; - - -export default function Header() { - const { user, setUser } = React.useContext(UserContext); - - const container: any = useRef(null); - const adminContainer: any = useRef(null); - - const [showUserMenu, setShowUserMenu] = useState(false); - const [currentHeader, setCurrentHeader] = useState(''); - - const [showMobileMenu, setShowMobileMenu] = useState(false); - - const [showAdminMenu, setShowAdminMenu] = useState(false); - - const defaultUserPhotoURL = - 'https://www.gravatar.com/avatar/00000000000000000000000000000000'; - const userPhotoURL = user.picture || defaultUserPhotoURL; - - const hasAdminAccess = user.roles?.includes('Admin'); +import hamburger_menu from '../../images/dash/hamburger_menu.svg'; +import HamburgerNav from '../hamburger-nav/HamburgerNav'; - useEffect(() => { - const currentUrl = window.location.pathname.split('/')[1]; - console.log(currentUrl); - setCurrentHeader(currentUrl); - }, []); +import crystalBall from '../../images/dash/crystal-ball.svg'; +import lineList from '../../images/dash/line-md_list-3-filled.svg'; +import hankHill from '../../images/dash/Hank_Hill.webp'; - useEffect(() => { - const handleOutsideClick = (event: MouseEvent) => { - if (!container?.current?.contains(event.target)) { - if (!showUserMenu) return; - setShowUserMenu(false); - } - }; +import firebase from 'firebase/app' +import { auth } from '../../firebase/firebase'; +import { signOut } from 'firebase/auth'; - window.addEventListener('click', handleOutsideClick); - return () => window.removeEventListener('click', handleOutsideClick); - }, [showUserMenu, container]); - - useEffect(() => { - const handleOutsideClick = (event: MouseEvent) => { - if (!adminContainer?.current?.contains(event.target)) { - if (!showAdminMenu) return; - setShowAdminMenu(false); - } - }; - - window.addEventListener('click', handleOutsideClick); - return () => window.removeEventListener('click', handleOutsideClick); - }, [showAdminMenu, adminContainer]); - - const handleLogout = () => { - setUser({ firstName: null, lastName: null, id: null, joinDate: null, email: null, userType: 'Reader', roles: ['None'] }); - localStorage.clear(); - } +import { Logo } from './Logo'; +import './header.css' - const onMenuItemClick = (path: any) => { - setShowUserMenu(false); - setShowAdminMenu(false); - setShowMobileMenu(false); - setCurrentHeader(path); +export default function Header() { + const { user, setUser } = React.useContext(UserContext); + const [error, setError] = useState(null); + const [showSidebar, setShowSidebar] = useState(false); + const toggleSidebar = () => { + setShowSidebar(!showSidebar); }; - function classNames(...classes: any[]) { - return classes.filter(Boolean).join(' '); - } - - return ( -
-
- - -
-
- ) - } \ No newline at end of file + + ); +} + +function setError(message: any) { + throw new Error('Function not implemented.'); +} diff --git a/apps/frontend/src/app/components/logo/SignUpLogo.css b/apps/frontend/src/app/components/logo/SignUpLogo.css new file mode 100644 index 0000000..5e540d6 --- /dev/null +++ b/apps/frontend/src/app/components/logo/SignUpLogo.css @@ -0,0 +1,56 @@ + +.signupLogoContainer { + display: flex; + justify-content: center; + top: 20px; + position: relative; +} +.signupLogo { + width: 196px; + height: 100px; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + column-gap: 7px; +} +.signupW { + color: #000; + font-family: Fontspring-DEMO-capitana-medium; + font-size: 40px; + font-style: normal; + font-weight: 500; + line-height: normal; +} +.signupListThreeFilled { + width: 40px; + height: 40px; + flex-shrink: 0; + fill: var(--Primary-Blue, #148CFB); +} +.signupL { + display: flex; + width: 23px; + height: 48px; + flex-direction: column; + justify-content: space-evenly; + flex-shrink: 0; + color: #000; + font-family: Fontspring-DEMO-capitana-medium; + font-size: 40px; + font-style: normal; + font-weight: 500; + line-height: normal; +} +.signupVector { + width: 3px; + height: 70px; + flex-shrink: 0; + stroke-width: 3px; + stroke: #000; +} +.signupCrystalBall { + width: 40px; + height: 40px; + flex-shrink: 0; +} diff --git a/apps/frontend/src/app/components/logo/SignUpLogo.tsx b/apps/frontend/src/app/components/logo/SignUpLogo.tsx new file mode 100644 index 0000000..77efd7d --- /dev/null +++ b/apps/frontend/src/app/components/logo/SignUpLogo.tsx @@ -0,0 +1,21 @@ +import loginListThreeFilled from "../../images/logos/loginListThreeFilled.svg" +import loginVector from "../../images/logos/loginVector.svg" +import loginCrystalBall from "../../images/logos/loginCrystalBall.svg" + +import './SignUpLogo.css' +export default function SignUpLogo() { + + return ( + <> +
+
+

W

+ +

L

+ + +
+
+ + ) +} diff --git a/apps/frontend/src/app/images/dash/Hank_Hill.webp b/apps/frontend/src/app/images/dash/Hank_Hill.webp new file mode 100644 index 0000000..c252172 Binary files /dev/null and b/apps/frontend/src/app/images/dash/Hank_Hill.webp differ diff --git a/apps/frontend/src/app/images/dash/b_smith.svg b/apps/frontend/src/app/images/dash/b_smith.svg new file mode 100644 index 0000000..b52c3f5 --- /dev/null +++ b/apps/frontend/src/app/images/dash/b_smith.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/src/app/images/dash/balance_btn.svg b/apps/frontend/src/app/images/dash/balance_btn.svg new file mode 100644 index 0000000..1b35b74 --- /dev/null +++ b/apps/frontend/src/app/images/dash/balance_btn.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/frontend/src/app/images/dash/cashflow_btn.svg b/apps/frontend/src/app/images/dash/cashflow_btn.svg new file mode 100644 index 0000000..d433ddf --- /dev/null +++ b/apps/frontend/src/app/images/dash/cashflow_btn.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/frontend/src/app/images/dash/close_button.png b/apps/frontend/src/app/images/dash/close_button.png new file mode 100644 index 0000000..609058d Binary files /dev/null and b/apps/frontend/src/app/images/dash/close_button.png differ diff --git a/apps/frontend/src/app/images/dash/crystal-ball.svg b/apps/frontend/src/app/images/dash/crystal-ball.svg new file mode 100644 index 0000000..65e3e85 --- /dev/null +++ b/apps/frontend/src/app/images/dash/crystal-ball.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/frontend/src/app/images/dash/hamburger_menu.svg b/apps/frontend/src/app/images/dash/hamburger_menu.svg new file mode 100644 index 0000000..71fa607 --- /dev/null +++ b/apps/frontend/src/app/images/dash/hamburger_menu.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/frontend/src/app/images/dash/line-md_list-3-filled.svg b/apps/frontend/src/app/images/dash/line-md_list-3-filled.svg new file mode 100644 index 0000000..67a8e1e --- /dev/null +++ b/apps/frontend/src/app/images/dash/line-md_list-3-filled.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/app/images/dash/net_worth.svg b/apps/frontend/src/app/images/dash/net_worth.svg new file mode 100644 index 0000000..b3d5d79 --- /dev/null +++ b/apps/frontend/src/app/images/dash/net_worth.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/src/app/images/dash/profile.svg b/apps/frontend/src/app/images/dash/profile.svg new file mode 100644 index 0000000..7de4c5c --- /dev/null +++ b/apps/frontend/src/app/images/dash/profile.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/frontend/src/app/images/dash/text.svg b/apps/frontend/src/app/images/dash/text.svg new file mode 100644 index 0000000..b72850d --- /dev/null +++ b/apps/frontend/src/app/images/dash/text.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/src/app/images/dash/user_btn.svg b/apps/frontend/src/app/images/dash/user_btn.svg new file mode 100644 index 0000000..a408651 --- /dev/null +++ b/apps/frontend/src/app/images/dash/user_btn.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/frontend/src/app/images/logos/TrashCan.svg b/apps/frontend/src/app/images/logos/TrashCan.svg new file mode 100644 index 0000000..44c6f8d --- /dev/null +++ b/apps/frontend/src/app/images/logos/TrashCan.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/src/app/images/logos/circleI.svg b/apps/frontend/src/app/images/logos/circleI.svg new file mode 100644 index 0000000..05cc11f --- /dev/null +++ b/apps/frontend/src/app/images/logos/circleI.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/frontend/src/app/images/logos/circlePlus.svg b/apps/frontend/src/app/images/logos/circlePlus.svg new file mode 100644 index 0000000..1d6388f --- /dev/null +++ b/apps/frontend/src/app/images/logos/circlePlus.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/frontend/src/app/images/logos/downArrow.svg b/apps/frontend/src/app/images/logos/downArrow.svg new file mode 100644 index 0000000..1234c6a --- /dev/null +++ b/apps/frontend/src/app/images/logos/downArrow.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/frontend/src/app/images/logos/upArrow.svg b/apps/frontend/src/app/images/logos/upArrow.svg new file mode 100644 index 0000000..535257a --- /dev/null +++ b/apps/frontend/src/app/images/logos/upArrow.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/frontend/src/app/views/about/about.tsx b/apps/frontend/src/app/views/about/about.tsx index adb8e5a..dc8f55f 100644 --- a/apps/frontend/src/app/views/about/about.tsx +++ b/apps/frontend/src/app/views/about/about.tsx @@ -1,257 +1,38 @@ -import { useState } from 'react' -import { Dialog } from '@headlessui/react' -// import { Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline' -const navigation = [ - { name: 'Product', href: '#' }, - { name: 'Features', href: '#' }, - { name: 'Resources', href: '#' }, - { name: 'Company', href: '#' }, -] -const stats = [ - { label: 'Transactions every 24 hours', value: '44 million' }, - { label: 'Assets under holding', value: '$119 trillion' }, - { label: 'New users annually', value: '46,000' }, -] -const values = [ - { - name: 'Be world-class', - description: - 'Aut illo quae. Ut et harum ea animi natus. Culpa maiores et sed sint et magnam exercitationem quia. Ullam voluptas nihil vitae dicta molestiae et. Aliquid velit porro vero.', - }, - { - name: 'Share everything you know', - description: - 'Mollitia delectus a omnis. Quae velit aliquid. Qui nulla maxime adipisci illo id molestiae. Cumque cum ut minus rerum architecto magnam consequatur. Quia quaerat minima.', - }, - { - name: 'Always learning', - description: - 'Aut repellendus et officiis dolor possimus. Deserunt velit quasi sunt fuga error labore quia ipsum. Commodi autem voluptatem nam. Quos voluptatem totam.', - }, - { - name: 'Be supportive', - description: - 'Magnam provident veritatis odit. Vitae eligendi repellat non. Eum fugit impedit veritatis ducimus. Non qui aspernatur laudantium modi. Praesentium rerum error deserunt harum.', - }, - { - name: 'Take responsibility', - description: - 'Sit minus expedita quam in ullam molestiae dignissimos in harum. Tenetur dolorem iure. Non nesciunt dolorem veniam necessitatibus laboriosam voluptas perspiciatis error.', - }, - { - name: 'Enjoy downtime', - description: - 'Ipsa in earum deserunt aut. Quos minus aut animi et soluta. Ipsum dicta ut quia eius. Possimus reprehenderit iste aspernatur ut est velit consequatur distinctio.', - }, -] -const team = [ - { - name: 'Michael Foster', - role: 'Co-Founder / CTO', - imageUrl: - 'https://images.unsplash.com/photo-1519244703995-f4e0f30006d5?ixlib=rb-=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=8&w=1024&h=1024&q=80', - }, - // More people... -] -const blogPosts = [ - { - id: 1, - title: 'Vel expedita assumenda placeat aut nisi optio voluptates quas', - href: '#', - description: - 'Illo sint voluptas. Error voluptates culpa eligendi. Hic vel totam vitae illo. Non aliquid explicabo necessitatibus unde. Sed exercitationem placeat consectetur nulla deserunt vel. Iusto corrupti dicta.', - imageUrl: - 'https://images.unsplash.com/photo-1496128858413-b36217c2ce36?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=3603&q=80', - date: 'Mar 16, 2020', - datetime: '2020-03-16', - author: { - name: 'Michael Foster', - imageUrl: - 'https://images.unsplash.com/photo-1519244703995-f4e0f30006d5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80', - }, - }, - // More posts... -] +import React, { useEffect, useState } from 'react'; +import { getAuth, onAuthStateChanged, User } from 'firebase/auth'; -export const About = () => { - const [mobileMenuOpen, setMobileMenuOpen] = useState(false) +export const About: React.FC = () => { + const [user, setUser] = useState(null); - return ( -
-
- {/* Hero section */} -
- -
+ return ( +
+ {user ? ( + <> +

True

+ {user && ( +
    + {Object.entries(user).map(([key, value]) => ( +
  • + {key}: {value && value.toString()} +
  • + ))} +
+ )} + + ) : ( +

False

+ )}
- ) -} + ); +}; \ No newline at end of file diff --git a/apps/frontend/src/app/views/assets/AssetContext.tsx b/apps/frontend/src/app/views/assets/AssetContext.tsx new file mode 100644 index 0000000..34a8527 --- /dev/null +++ b/apps/frontend/src/app/views/assets/AssetContext.tsx @@ -0,0 +1,105 @@ +import React, { createContext, useState, useEffect, useContext } from "react" +import axios from "axios"; + + + +interface Asset { + id: string; + asset: string; + amount: number; + } +interface AssetContextType { + assets: Asset[]; + setAssets: React.Dispatch>; + addNewAsset: (newAsset: Asset) => void; + saveAssets: (assets: Asset[]) => void; + removeAsset: (asset: Asset) => void; + getAllAssets: () => void; + } + +export const AssetContext = createContext(undefined) + + +export const AssetProvider: React.FC<{ children: React.ReactNode }> = ({ + children, + }) => { + const [assets, setAssets] = useState([]); + + const addNewAsset = (newAsset: Asset) => { + return setAssets((prev: Asset[]) => { + const newAssets: Asset[] = [...prev, newAsset]; + setAssets(newAssets); + saveAssets(newAssets); + console.log(newAssets); + return newAssets; + }); + }; + + const saveAssets = (assets: Asset[]) => { + try { + localStorage.setItem('assets', JSON.stringify(assets)); + } catch (err) { + console.error('error saving to local storage', err); + } + }; + + const removeAsset = (asset: Asset) => + setAssets((prev) => { + const newAssets = prev.filter((as) => as.id !== asset.id); + saveAssets(newAssets); + console.log('deleted asset'); + return newAssets; + }); + + const getAllAssets = () => { + try { + const storedAssetsString: string | null = + localStorage.getItem('assets'); + + if (storedAssetsString !== null) { + const storedAssets: Asset[] | Asset = + JSON.parse(storedAssetsString); + + if (Array.isArray(storedAssets)) { + setAssets(storedAssets); + } else { + setAssets([storedAssets]); + console.log('not saved'); + } + } + } catch (err) { + console.error(err); + } + }; + + + + + + + useEffect(() => { + getAllAssets(); + }, []); + + // type AssetContextType = { + // assets, + // setAssets, + // addNewAsset, + // saveAssets, + // removeAsset, + // getAllAssets, + // }; + + + + return ( + // + // {children} + // + +
+

test

+
+ ) + } + diff --git a/apps/frontend/src/app/views/assets/AssetList.tsx b/apps/frontend/src/app/views/assets/AssetList.tsx new file mode 100644 index 0000000..c3f9bb9 --- /dev/null +++ b/apps/frontend/src/app/views/assets/AssetList.tsx @@ -0,0 +1,23 @@ +// import { useContext } from 'react'; +// import { AssetContext } from './AssetContext'; +// import { AssetContext } from './Index'; +// import { UpdateAssets } from './UpdateAssets'; + +const AssetList = (props: any) => { +// const { assets } = useContext(AssetContext); + + return ( + //
    + // {props.asset.map((asset: any) => ( + // // + // ))} + //
+ // ); + +
+

assets

+
+ ) +}; + +export default AssetList; diff --git a/apps/frontend/src/app/views/assets/Assets.tsx b/apps/frontend/src/app/views/assets/Assets.tsx new file mode 100644 index 0000000..7983d54 --- /dev/null +++ b/apps/frontend/src/app/views/assets/Assets.tsx @@ -0,0 +1,114 @@ +import { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { AuthLayout } from '../../components/authlayout/AuthLayout'; +import hamburger_menu from '../../images/dash/hamburger_menu.svg'; +import hankHill from '../../images/dash/Hank_Hill.webp'; +import image103 from '../../images/logos/loginGroup103.svg'; +import HamburgerNav from '../../components/hamburger-nav/HamburgerNav'; +import DropdownSelect from '../../components/dropdown-select/DropdownSelect'; +import './assets.css'; +import styles from '../../app.module.scss'; + +export const Assets = () => { + const navigate = useNavigate(); + const initInputs = { + select_month: '', + select_year: '', + }; + + const [inputs, setInputs] = useState(initInputs); + // const [isOpen, setIsOpen] = useState(false); + const [isDisabled, setIsDisabled] = useState(!(initInputs.select_month && initInputs.select_year)); + + + const handleChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + + setInputs((prevInputs) => ({ ...prevInputs, [name]: value })); + console.log(inputs); + }; + + const handleSubmit = () => { + navigateToNewPage(); + console.log('Submitted!'); + }; + + const navigateToNewPage = () => { + console.log('new page'); + navigate('/assets/input'); + }; + + useEffect(() => { + // Update isDisabled whenever select_month or select_year changes + setIsDisabled(!(inputs.select_month && inputs.select_year)); + }, [inputs]); + return ( + + +
+

+ Let's get an understanding of your assets this month +

+
+ + + +
+

Select Month

+
+ +
+
+ +
+

Select Year

+
+
+ +
+
+ +
+
+ ); +}; + diff --git a/apps/frontend/src/app/views/assets/AssetsInput.tsx b/apps/frontend/src/app/views/assets/AssetsInput.tsx new file mode 100644 index 0000000..4d4bba4 --- /dev/null +++ b/apps/frontend/src/app/views/assets/AssetsInput.tsx @@ -0,0 +1,412 @@ +import { useState, ChangeEvent, useContext } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { AuthLayout } from '../../components/authlayout/AuthLayout'; +import hamburger_menu from '../../images/dash/hamburger_menu.svg'; +import hankHill from '../../images/dash/Hank_Hill.webp'; +import HamburgerNav from '../../components/hamburger-nav/HamburgerNav'; +import circlePlus from '../../images/logos/circlePlus.svg'; +import upArrow from '../../images/logos/upArrow.svg'; +import downArrow from '../../images/logos/downArrow.svg'; +import circleI from '../../images/logos/circleI.svg'; +import './assets.css'; +import styles from '../../app.module.scss'; + +import { InfoTooltip } from './InfoTooltip'; +import toast, { Toaster } from 'react-hot-toast'; +import { v4 as uuidv4 } from 'uuid'; +import { AssetContext, } from './AssetContext'; + + +interface Asset { + id: string; + asset: string; + amount: number; +} + +export const AssetsInput: React.FC = (props: any) => { + const navigate = useNavigate(); + // const { addNewAsset} = useContext(AssetContext) as AssetContextType; + const [showInputForm, setShowInputForm] = useState(''); + const [showCategoryDropdown, setShowCategoryDropdown] = + useState(false); + const [inputs, setInputs] = useState({ id: uuidv4(), asset: '', amount: 0 }) + const [value, setValue] = useState(0); + + + // const assetContext = useContext(AssetContext); + // const [isOpen, setIsOpen] = useState(false); + + + + + // if (!assetContext) { + // // Handle case where context is undefined + // return null; + + // } + + + // const { saveAssets } = assetContext as AssetContextType + + const saveAssets = (assets: Asset[]) => { + console.log("asset saved! Or will be once we have the API route set up.") + }; + + +// console.log(assets) +// interface AssetsInputProps { +// saveAssets: (assets: Asset[]) => void; // Adjust the type of saveAssets function +// } + + const navigateToNewPage = () => { + // console.log('new page'); + navigate('/assets/update'); + }; + + const assetToast = () => { + console.log('asset added'); + + toast('Asset added', { + position: 'top-center', + icon: '👍', + style: { + borderRadius: '20px', + backgroundColor: '#008000', + color: 'white', + }, + }); + }; + + const handleUpButtonClick = (sectionName: string) => { + //pops out input form based on the sectionName selected + setShowInputForm(sectionName); + }; + + const handleAddAssetClick = (sectionName: string) => { + const { id, asset, amount } = inputs; + setValue(value + amount); + console.log(inputs) + console.log(asset) + // addNewAsset({id, asset, amount}) + + saveAssets([{ id, asset, amount}]) + setInputs({ + id: uuidv4(), + asset: '', + amount: 0, + + }); + assetToast(); + + }; + + + + const handleSelectCategory = (e: any) => { + e.preventDefault(); + //populates drop down menu specific to the sectionName selected + setShowCategoryDropdown((prevState) => !prevState); + console.log('category selected!'); + }; + + const handleUpdateAssets = () => { + navigateToNewPage(); + //routes to slide to delete page + //which lists all assets and allows them to be deleted + //once deleted, they will be removed from the total (value) amount + console.log('slide to delete page appears'); + }; + + const handleDownArrowClick = (sectionName: string) => { + //collapses the input form for the selected sectionName + console.log('form collapses'); + }; + + const handleChange = (e: ChangeEvent) => { + const { name, value } = e.target; + setInputs((prevInputs) => ({ ...prevInputs, [name]: value })); + }; + + console.log(showCategoryDropdown); + console.log(inputs); + + console.log(value); + return ( + + +
+
+

+ Let's get an understanding of your assets this month +

+
+
+ +
+
+
+
+

Personal

+
+ + + {showInputForm === 'personal' && ( +
+
+ + setInputs((prevInputs) => ({ + ...prevInputs, + asset: e.target.value, + })) + } + > + + setInputs((prevInputs) => ({ + ...prevInputs, + amount: parseFloat(e.target.value) || 0, + })) + } + > + + {showCategoryDropdown && ( + + )} + + +
+
+ )} +
+
+
+
+

Investable

+
+ + + + {showInputForm === 'investable' && ( +
+
+ + setInputs((prevInputs) => ({ + ...prevInputs, + asset: e.target.value, + })) + } + > + + setInputs((prevInputs) => ({ + ...prevInputs, + amount: parseFloat(e.target.value) || 0, + })) + } + > + + {showCategoryDropdown && ( + + )} + + +
+
+ )} +
+
+
+
+

Non-Investable

+
+ + + + {showInputForm === 'non-investable' && ( +
+
+ + setInputs((prevInputs) => ({ + ...prevInputs, + asset: e.target.value, + })) + } + > + + setInputs((prevInputs) => ({ + ...prevInputs, + amount: parseFloat(e.target.value) || 0, + })) + } + > + + {showCategoryDropdown && ( + + )} + + +
+
+ )} +
+
+
+
+

Value: {value}.00

+
+ + +
+
+
+ ); +}; diff --git a/apps/frontend/src/app/views/assets/InfoTooltip.tsx b/apps/frontend/src/app/views/assets/InfoTooltip.tsx new file mode 100644 index 0000000..c65ca2c --- /dev/null +++ b/apps/frontend/src/app/views/assets/InfoTooltip.tsx @@ -0,0 +1,34 @@ +import { useState } from 'react'; + + +import './assets.css'; + + +export interface InfoTooltipProps { + tooltiptext: string; + circleI?: string; + children?: React.ReactNode; +} + +export const InfoTooltip: React.FC = ({ + tooltiptext, + circleI, + children, +}) => { + const [showToolTip, setShowToolTip] = useState(false); + return ( +
+ icon setShowToolTip(true)} + onMouseLeave={() => setShowToolTip(false)} + style={{ cursor: 'pointer' }} + /> + {children} + + {showToolTip &&
{tooltiptext}
} + {children} +
+ ); +}; diff --git a/apps/frontend/src/app/views/assets/Toaster.tsx b/apps/frontend/src/app/views/assets/Toaster.tsx new file mode 100644 index 0000000..baa3ada --- /dev/null +++ b/apps/frontend/src/app/views/assets/Toaster.tsx @@ -0,0 +1,6 @@ +import toast, { Toaster } from 'react-hot-toast'; + +{ + /* { +// const { assets, removeAsset } = useContext(AssetContext); + +// return ( +//
+// Slide to delete +// {assets.map((asset) => ( +//
+//

Asset: {asset.asset}

+//

Amount: {asset.amount}

+//
+// +//
+//

+//
+// ))} +//
+// ); +// }; diff --git a/apps/frontend/src/app/views/assets/assets.css b/apps/frontend/src/app/views/assets/assets.css new file mode 100644 index 0000000..7b39099 --- /dev/null +++ b/apps/frontend/src/app/views/assets/assets.css @@ -0,0 +1,275 @@ +.headline { +width: 345px; +height: 110px; +top: 100px; +left: 24px; +font-family: FONTSPRING DEMO - Capitana Extrabold; +font-size: 20px; +font-weight: 800; +line-height: 30px; +letter-spacing: 0px; +text-align: center; +padding-left: 60px; +padding-right: 60px; +} + +.button-div { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 30vh; +} +.select_month_div { + width: 345px; + height: 120px; + top: 329px; + left: 24px; + border-radius: 8px; + border: 4px, 0px, 4px, 0px; +} + + + +.select_year_div { + width: 345px; +height: 120px; +top: 505px; +left: 24px; +border: 4px, 0px, 4px, 0px + +} + +.month_input { +width: 345px; +height: 120px; +margin-bottom:20px; +border-radius: 8px; +border: 4px, 0px, 4px, 0px; +border: 1px solid black; +} + +.year_input { + width: 345px; + height: 120px; + top: 329px; + left: 24px; + border-radius: 8px; + border: 4px, 0px, 4px, 0px; + border: 1px solid black; + margin-bottom: 60px; + } + + +.p_select_year { + width: 345 px; + height: 16px; + +font-family: Sofia Pro; +font-size: 16px; +font-weight: 300; +line-height: 16px; +letter-spacing: 0px; +text-align: center; +margin-left: 24px; +padding-bottom: 30px; +} + +.p_select_month { + width: 345 px; + height: 16px; + +font-family: Sofia Pro; +font-size: 16px; +font-weight: 300; +line-height: 16px; +letter-spacing: 0px; +text-align: center; +margin-left: 24px; +padding-bottom: 30px; +} + + + +.asset_type_button { + width: Hug (142px); +height: Fixed (40px); +top: 281px; +left: 126px; +padding: 10px; +border-radius: 80px; +gap: 10px; +background: #148CFB; +color: white; +margin-bottom: 2px; +} + +.add_btn { + width: Hug (142px); + height: Fixed (40px); + top: 281px; + left: 126px; + padding: 10px; + border-radius: 80px; + gap: 10px; + background: #148CFB; + color: white; + margin-bottom: 2px; +} + +.button-and-form-container { + display: flex; + align-items: center; +} + +.button-and-form-container input { + margin-right: 10px; +} +.form-container { + + margin-left: 20px; +} + +.value_div { + padding-top:20px; + +} + +.asset_input_main_div { + width: Hug (345px); +height: Hug (390px); + +margin-left: 24px; +gap: 32px; + +} + +.asset_p { + font-family: FONTSPRING DEMO - Capitana Extrabold; +font-size: 20px; +font-weight: 800; +line-height: 30px; +letter-spacing: 0px; +flex-grow: 1; +text-align: center; +margin-right: 10px; +} + +.horizontal_line { + border-top: 4px solid black; + margin-left: 10px; + margin-right: 24px; + margin-top:10px; + margin-bottom: 10px; + +} +.btn-container { + display: flex; + justify-content: center; +} + +.asset_button_large { + width: 345px; +height: 56px; +top: 681px; +left: 24px; +padding: 17px, 0px, 16px, 0px; +border-radius: 100px; +background: #148CFB; +color: white; +font-family: FONTSPRING DEMO - Capitana Extrabold; +font-size: 20px; +margin-bottom: 2px; +margin-top: 20px; + +} + +.asset-form { + width: 300px; + margin: 20px auto; + text-align: center; +} + +.asset-form-button { + + width: 300px; + height: 50px; + border-radius: 100px; + background: #148CFB; + color: white; + font-family: FONTSPRING DEMO - Capitana Extrabold; + font-size: 20px; + margin-bottom: 2px; + margin-top: 20px; +} + +.asset_p_value { +font-family: FONTSPRING DEMO - Capitana Extrabold; +font-size: 20px; +font-weight: 800; +line-height: 30px; +letter-spacing: 0px; +text-align: right; +margin-right: 80px; + +} + +.add-button { + max-width: none !important; +} + + +.asset-form-input { + border-radius: 7px; +} + +/* .asset-modal-div { + position: relative; + top: 50%; + left: 50%; + width: 200px; + transform: 'translate(-50%, -50%)'; + background: #008000; + padding: 20px; + border-radius: 20px; + text-align: center; + +} */ + +.asset-modal-text { + font-family: Sofia Pro; + font-size: 16px; + font-weight: bold; + color: white; +} + +modal-btn { + + + font-family: Sofia Pro; + font-size: 16px; + font-weight: bold; + color: white; +} + +.info-tool-tip { + position: relative; + display: inline-block; +} + +.tooltip-div { + position: absolute; + background: #148CFB; + width: 364px; + height: 340px; + border-radius: 32px; + color: white; + font: 16px Sofia Pro; + margin-top: 15px; + margin-left: 31px; + padding: 10px; + white-space: pre-line; + + + +} \ No newline at end of file diff --git a/apps/frontend/src/app/views/assets/uuid.d.ts b/apps/frontend/src/app/views/assets/uuid.d.ts new file mode 100644 index 0000000..f9d6a6d --- /dev/null +++ b/apps/frontend/src/app/views/assets/uuid.d.ts @@ -0,0 +1 @@ +declare module 'uuid'; diff --git a/apps/frontend/src/app/views/dashboard/Dashboard.tsx b/apps/frontend/src/app/views/dashboard/Dashboard.tsx index 228e1da..22bd3d6 100644 --- a/apps/frontend/src/app/views/dashboard/Dashboard.tsx +++ b/apps/frontend/src/app/views/dashboard/Dashboard.tsx @@ -1,11 +1,50 @@ -import React from "react" +// Dashboard.js +import React from 'react'; +import { AuthLayout } from '../../components/authlayout/AuthLayout'; +import hankHill from '../../images/dash/Hank_Hill.webp'; +import './dashboard.css'; export const Dashboard = () => { - return ( -
-

DASHBOARD PAGE

-
- ) -} \ No newline at end of file + +
e.preventDefault()}> +
+ Frame2 + +
Hi, Bsmith
+ +
+
###.##
+
Net worth
+
+ +
+ + + +
+
+
+
+ ); +}; diff --git a/apps/frontend/src/app/views/dashboard/dashboard.css b/apps/frontend/src/app/views/dashboard/dashboard.css new file mode 100644 index 0000000..8f9ce0c --- /dev/null +++ b/apps/frontend/src/app/views/dashboard/dashboard.css @@ -0,0 +1,139 @@ +.dashboard-root { + width: 100%; + height: auto; + background-color: white; + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 0; + padding: 0; + border-radius: 0; +} + +.dashboard-content { + padding-top: 5vh; + display: grid; + grid-template-rows: auto 1fr 1fr 1fr; + grid-template-columns: 1fr; + height: 100vh; + grid-row: 3 / span 10; + grid-column: 1 / span 1; + display: flex; + flex-direction: column; + align-items: center; + gap: 5vh; +} + +.hamburger-menu-section { + padding-top: 8vh; + grid-row: 1 / span 1; + grid-column: 1 / span 1; + width: 80%; + align-items: center; +} + +.profile-image { + width: 2rem; + border-radius: 0.75rem; +} + +.greeting-text { + font-size: 2rem; + margin-top: 1rem; + text-align: center; + font-weight: 500; +} + +.net-worth-section { + display: flex; + flex-direction: column; + margin-left: 20px; + gap: 6px; + justify-content: center; + font-family: 'Raleway', sans-serif; +} + +.net-worth-value { + font-size: 2rem; + font-weight: 800; + text-align: center; +} + +.net-worth-label { + font-family: 'Raleway', sans-serif; + width: 100%; + font-size: 1.5rem; + font-weight: 300; + justify-content: center; + text-align: center; +} + +.buttons-section { + display: flex; + flex-direction: column; + justify-content: center; + gap: 1.5rem; + width: 75%; + align-items: center; + text-align: center; +} + +.dashboard-button { + font-size: 1rem; + font-weight: bold; + font-family: 'Sofia Pro', sans-serif; + color: white; + background-color: #148cfb; + display: flex; + flex-direction: column; + justify-content: center; + width: 100%; + height: 3rem; + cursor: pointer; + align-items: center; + text-align: center; + border-radius: 100px; +} + +.dashboard-button:hover { + font-size: 1.5rem; +} + +.first-time-user-text { + padding: 50px; + margin-top: 10vh; + font-family: 'Sofia Pro', sans-serif; + font-size: 1rem; + font-weight: 800; + letter-spacing: 0px; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + border-width: 2px; + padding: 0.5rem 1rem; + height: 2rem; + border-color: black; + border-radius: 20px; +} + +.first-time-user-text { + @apply lg:hidden +} +.first-time-user-text:hover { + @apply underline; +} + +.dashboard-root { + display: flex; + position: relative; +} + +.hamburger-button { + position: absolute; + top: 20px; + left: 20px; + z-index: 100; +} + + diff --git a/apps/frontend/src/app/views/router/router.tsx b/apps/frontend/src/app/views/router/router.tsx index 8076be6..1bdfa81 100644 --- a/apps/frontend/src/app/views/router/router.tsx +++ b/apps/frontend/src/app/views/router/router.tsx @@ -1,32 +1,39 @@ import React from 'react'; -import { Route, Routes } from 'react-router-dom'; +import { Route, Routes, Navigate } from 'react-router-dom'; import { AccountPage } from '../account/AccountsPage'; import { BlogsPage } from '../blogs/BlogsPage'; import { Home } from '../home/home'; import {About} from '../about/about'; import {Login} from '../login/Login'; -import { Navigate } from "react-router-dom"; +// import { Navigate } from "react-router-dom"; import IndividualBlogPage from '../blogs/IndividualBlogPage'; import AdminPage from '../admin/AdminPage'; import { ForgotPassword } from '../forgotPassword/ForgotPassword'; import {SignUp} from "../signup/SignUp" import { Dashboard } from '../dashboard/Dashboard'; +import { Assets } from "../assets/Assets" +import { AssetsInput } from '../assets/AssetsInput'; +// import { UpdateAssets } from '../assets/UpdateAssets'; + export default function Router() { return ( } /> - } /> + } /> } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> } /> } /> } /> } /> + } /> + } /> + {/* } /> */} ); } \ No newline at end of file diff --git a/apps/frontend/src/app/views/signup/SignUp.tsx b/apps/frontend/src/app/views/signup/SignUp.tsx index 538e803..b298a2d 100644 --- a/apps/frontend/src/app/views/signup/SignUp.tsx +++ b/apps/frontend/src/app/views/signup/SignUp.tsx @@ -1,121 +1,74 @@ -import React, { useState, useContext, useEffect } from "react" -import "./signup.css" -import loginListThreeFilled from "../../images/logos/loginListThreeFilled.svg" -import loginVector from "../../images/logos/loginVector.svg" -import loginCrystalBall from "../../images/logos/loginCrystalBall.svg" -import loginGroup103 from "../../images/logos/loginGroup103.svg" -import { createUserWithEmailAndPassword } from "firebase/auth" +import { useState, useContext, FormEvent } from "react" import { auth } from "../../firebase/firebase" import { useNavigate } from 'react-router-dom'; -import { AuthLayout } from '../../components/authlayout/AuthLayout'; -import { signUpUser, getUsersFromSearch } from "../../api-client/apiModules/users" import { UserContext } from "../../../app/app" +import SignUpLogo from "../../components/logo/SignUpLogo"; +import { createUserWithEmailAndPassword } from 'firebase/auth'; +import "./signup.css" + export const SignUp = () => { const context = useContext(UserContext) - const [isDisabled, setIsDisabled] = React.useState(true) - const [authError, setAuthError] = React.useState(false) - const [emailError, setEmailError] = React.useState(true) - const navigate = useNavigate() - const [firstName, setFirstName] = React.useState('') - const [lastName, setLastName] = React.useState('') - const [username, setUsername] = React.useState('') - const [email, setEmail] = React.useState('') - const [password, setPassword] = React.useState('') - const [inputError, setInputError] = React.useState(false) - + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [error, setError] = useState(''); - useEffect(() => { - const emailRegex = new RegExp(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/); - if (!emailRegex.test(email)) { - setIsDisabled(true) - } else if(password.length < 8) { - setIsDisabled(true) - } else { - setIsDisabled(false) - } - }) + const navigate = useNavigate(); - const signUp = async () => { - const response = await signUpUser({email, password, firstName, lastName, username}) - console.log(response) - if(response.success !== false) { - context.setUser(response) - navigate("/dashboard") - } else { - setAuthError(true) - alert(response.message) + const signUp = async (e: FormEvent) => { + e.preventDefault() + try { + await createUserWithEmailAndPassword(auth, email, password); + navigate('/dashboard') + } catch (error: any) { + setError(error.message); } - } - function whiteSpace() { - return {""} + }; + + const handlePasswordChange = (e: React.ChangeEvent) => { + setPassword(e.target.value) + if (confirmPassword !== e.target.value) { + setError("Passwords do not match") + return + } else setError("") + } + const handleConfirmPasswordChange = (e: React.ChangeEvent) => { + setConfirmPassword(e.target.value) + if (password !== e.target.value) { + setError("Passwords do not match") + return + } else setError("") } return ( - -
-
- +
+
+ +
+
signUp(e)}> +
+ + setEmail(e.target.value)} /> +
+
+ +
-
-
-

W

- -

L

- - -
+
+ +
-
-

{authError ? "* Indicates Required" : <> }

+
+ {error ?
{error}
:
}
-
-
-

{authError ? "*" : whiteSpace()}First Name

-
- setFirstName(e.target.value)}/> -
-
-
-

{authError ? "*" : whiteSpace()}Last Name

-
- setLastName(e.target.value)}/> -
-
-
-

{authError ? "*" : whiteSpace()}Username

-
- setUsername(e.target.value)}/> -
-
-
-

{authError ? "*" : whiteSpace()}Email

-
- setEmail(e.target.value)}/> -
-
-
-

{authError ? "Please enter a valid email address" : ""}

-
-
-

{authError ? "*" : whiteSpace()}Password

-
- setPassword(e.target.value)}/> -
-
-
-

{authError ? "Password must be at least 8 characters" : ""}

-
-
- -
+
+
-
- + +
) } \ No newline at end of file diff --git a/apps/frontend/src/app/views/signup/signup.css b/apps/frontend/src/app/views/signup/signup.css index 1f08b77..03b15d9 100644 --- a/apps/frontend/src/app/views/signup/signup.css +++ b/apps/frontend/src/app/views/signup/signup.css @@ -1,360 +1,62 @@ -.signupFirstNameContainer { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} -.signupFirstName { - color: #000; - font-feature-settings: 'clig' off, 'liga' off; - font-family: Sofia Pro; - font-size: 16px; - font-style: normal; - font-weight: 700; - line-height: normal; - padding-bottom: 4px; - width: 345px; -} -.signupFirstNameInput { - font-feature-settings: 'clig' off, 'liga' off; - font-family: DM Sans; - font-size: 20px; - font-style: normal; - font-weight: 700; - /* line-height: 28px; 140% */ - height: 28px; - border: none; - width: 320px; - justify-content: center; - align-items: center; - border: none; +.signupLayout { + display: grid; + grid-template-rows: 40px 97px 60px; } -.signupFirstNameInputContainer { - display: flex; - height: 40px; - padding: 6px 4px 4px 4px; - align-items: center; - border-radius: 8px; - border: 2px solid #6F6F6F; - background: #F6F6F6; - width: 345px; +.signupBodyContainer { + display: grid; + grid-template-rows: 102px 86px 98px 86px 20px 66px 20px 124px; } -.signupLastNameContainer { +.signupForm { display: flex; flex-direction: column; - justify-content: center; - align-items: center; -} -.signupLastName { - color: #000; - font-feature-settings: 'clig' off, 'liga' off; - font-family: Sofia Pro; - font-size: 16px; - font-style: normal; - font-weight: 700; - line-height: normal; - padding-bottom: 4px; - width: 345px; -} -.signupLastNameInput { - font-feature-settings: 'clig' off, 'liga' off; - font-family: DM Sans; - font-size: 20px; - font-style: normal; - font-weight: 700; - /* line-height: 28px; 140% */ - height: 28px; - border: none; - width: 320px; - justify-content: center; - align-items: center; -} -.signupLastNameInputContainer { - display: flex; - height: 40px; - padding: 6px 4px 4px 4px; align-items: center; - border-radius: 8px; - border: 2px solid #6F6F6F; - background: #F6F6F6; - width: 345px; + gap: 20px; + max-width: 500px; + margin: 0 auto; } -.signupUsernameContainer { +.formGroup { display: flex; flex-direction: column; - justify-content: center; - align-items: center; -} -.signupUsername { - color: #000; - font-feature-settings: 'clig' off, 'liga' off; - font-family: Sofia Pro; - font-size: 16px; - font-style: normal; - font-weight: 700; - line-height: normal; - padding-bottom: 4px; - width: 345px; -} -.signupUsernameInput { - font-feature-settings: 'clig' off, 'liga' off; - font-family: DM Sans; - font-size: 20px; - font-style: normal; - font-weight: 700; - /* line-height: 28px; 140% */ - height: 28px; - border: none; - width: 320px; - justify-content: center; - align-items: center; -} -.signupUsernameInputContainer { - display: flex; - height: 40px; - padding: 6px 4px 4px 4px; - align-items: center; - border-radius: 8px; - border: 2px solid #6F6F6F; - background: #F6F6F6; - width: 345px; -} -.signupEmailContainer { + align-items: flex-start; display: flex; flex-direction: column; justify-content: center; - align-items: center; + align-items: flex-start; } -.signupEmail { - color: #000; - font-feature-settings: 'clig' off, 'liga' off; - font-family: Sofia Pro; +label { font-size: 16px; - font-style: normal; - font-weight: 700; - line-height: normal; - padding-bottom: 4px; - width: 345px; -} -.signupEmailInput { - font-feature-settings: 'clig' off, 'liga' off; - font-family: DM Sans; - font-size: 20px; - font-style: normal; - font-weight: 700; - /* line-height: 28px; 140% */ - height: 28px; - border: none; - width: 320px; - justify-content: center; - align-items: center; + font-weight: bold; + margin-bottom: 4px; } -.signupEmailInputContainer { - display: flex; +.inputField { + width: 100%; height: 40px; - padding: 6px 4px 4px 4px; - align-items: center; - border-radius: 8px; + padding: 6px; border: 2px solid #6F6F6F; - background: #F6F6F6; - width: 345px; -} -.signupPasswordContainer { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -} -.signupPassword { - color: #000; - font-feature-settings: 'clig' off, 'liga' off; - font-family: Sofia Pro; - font-size: 16px; - font-style: normal; - font-weight: 700; - line-height: normal; - padding-bottom: 4px; - width: 345px; -} -.signupPasswordInput { - font-feature-settings: 'clig' off, 'liga' off; - font-family: DM Sans; - font-size: 20px; - font-style: normal; - font-weight: 700; - /* line-height: 28px; 140% */ - height: 28px; - border: none; - width: 320px; - justify-content: center; - align-items: center; -} -.signupPasswordInputContainer { - display: flex; - height: 40px; - padding: 6px 4px 4px 4px; - align-items: center; border-radius: 8px; - border: 2px solid #6F6F6F; background: #F6F6F6; - width: 345px; } -.signupButtonContainer { - display: flex; - justify-content: center; - align-items: center; +.error { + color: #f00; + font-size: 14px; + font-style: italic; + height: 20px; + z-index: 1; + } -.signupButton { - display: flex; - width: 345px; - height: 56px; - padding: 16px 0px 17px 0px; - justify-content: center; - align-items: center; +.submitButton { + width: 35vh; + height: 56px; /* Adjust height as needed */ + border: none; border-radius: 100px; background: #000; -} -.signupButtonText { color: #FFF; - font-feature-settings: 'clig' off, 'liga' off; - - /* Mobile Button Text */ - font-family: Sofia Pro; - font-size: 20px; - font-style: normal; - font-weight: 800; - line-height: normal; -} -.signupGroup103Container { + font-weight: bold; + cursor: pointer; display: flex; justify-content: center; - align-items: top; -} -.signupGroup103 { - width: 128px; - height: 39px; - flex-shrink: 0; -} -.signupLogoContainer { - display: flex; - justify-content: center; - top: 20px; - position: relative; -} -.signupLogo { - width: 196px; - height: 100px; - flex-shrink: 0; - display: flex; align-items: center; - justify-content: center; - column-gap: 7px; } -.signupW { - color: #000; - font-family: Fontspring-DEMO-capitana-medium; - font-size: 40px; - font-style: normal; - font-weight: 500; - line-height: normal; +.submitButton:hover { + background: #333; } -.signupListThreeFilled { - width: 40px; - height: 40px; - flex-shrink: 0; - fill: var(--Primary-Blue, #148CFB); -} -.signupL { - display: flex; - width: 23px; - height: 48px; - flex-direction: column; - justify-content: space-evenly; - flex-shrink: 0; - color: #000; - font-family: Fontspring-DEMO-capitana-medium; - font-size: 40px; - font-style: normal; - font-weight: 500; - line-height: normal; -} -.signupVector { - width: 3px; - height: 70px; - flex-shrink: 0; - stroke-width: 3px; - stroke: #000; -} -.signupCrystalBall { - width: 40px; - height: 40px; - flex-shrink: 0; -} -.signupLayout { - display: grid; - grid-template-rows: 40px 97px 60px; -} -.signupIndicatesRequiredContainer { - display: flex; - justify-content: center; - align-items: center; - padding-top: 34px; -} -.signupIndicatesRequired { - color: #F00; - font-feature-settings: 'clig' off, 'liga' off; - font-family: Arial; - font-size: 14px; - font-style: italic; - font-weight: 400; - line-height: normal; -} -.signupEmailErrorContainer { - width: 295px; - height: 16px; - top: 616px; - left: 24px; -} -.signupEmailError { - color: #FF0000; - font-family: Arial; - font-size: 14px; - font-style: italic; - font-weight: 400; - line-height: 6px; - letter-spacing: 0px; - text-align: left; - padding-left: 10px; -} -.signupPasswordErrorContainer { - width: 296px; - height: 16px; - top: 714px; - left: 24px; -} -.signupPasswordError { - color: #FF0000; - font-family: Arial; - font-size: 14px; - font-style: italic; - font-weight: 400; - line-height: 26px; - letter-spacing: 0px; - text-align: left; - padding-left: 10px; -} -.signupAsterisk { - font-family: Arial; - font-size: 14px; - font-style: italic; - font-weight: 400; - line-height: 16px; - letter-spacing: 0px; - text-align: left; - color: #ff0000; - padding-right: 4px; -} -.signupBodyContainer { - display: grid; - grid-template-rows: 102px 86px 98px 86px 20px 66px 20px 124px; -} \ No newline at end of file diff --git a/apps/frontend/src/main.tsx b/apps/frontend/src/main.tsx index 82507af..a81fe95 100644 --- a/apps/frontend/src/main.tsx +++ b/apps/frontend/src/main.tsx @@ -3,12 +3,15 @@ import * as ReactDOM from 'react-dom/client'; import { GoogleOAuthProvider } from '@react-oauth/google'; import { BrowserRouter } from 'react-router-dom'; +import Modal from 'react-modal' + import App from './app/app'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); +Modal.setAppElement('#root'); root.render( diff --git a/apps/frontend/src/react-modal.d.ts b/apps/frontend/src/react-modal.d.ts new file mode 100644 index 0000000..e1013c9 --- /dev/null +++ b/apps/frontend/src/react-modal.d.ts @@ -0,0 +1 @@ +declare module 'react-modal'; diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index c1e2dd4..cb510da 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -13,4 +13,4 @@ "compilerOptions": { "esModuleInterop": true } -} +} \ No newline at end of file diff --git a/cypress.config.ts b/cypress.config.ts new file mode 100644 index 0000000..17161e3 --- /dev/null +++ b/cypress.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from "cypress"; + +export default defineConfig({ + e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, + }, +}); diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 0000000..02e4254 --- /dev/null +++ b/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts new file mode 100644 index 0000000..698b01a --- /dev/null +++ b/cypress/support/commands.ts @@ -0,0 +1,37 @@ +/// +// *********************************************** +// This example commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add('login', (email, password) => { ... }) +// +// +// -- This is a child command -- +// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) +// +// declare global { +// namespace Cypress { +// interface Chainable { +// login(email: string, password: string): Chainable +// drag(subject: string, options?: Partial): Chainable +// dismiss(subject: string, options?: Partial): Chainable +// visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable +// } +// } +// } \ No newline at end of file diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts new file mode 100644 index 0000000..f80f74f --- /dev/null +++ b/cypress/support/e2e.ts @@ -0,0 +1,20 @@ +// *********************************************************** +// This example support/e2e.ts is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands' + +// Alternatively you can use CommonJS syntax: +// require('./commands') \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 25854d9..959bc18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "@tailwindcss/typography": "^0.5.9", "axios": "^1.6.0", "clsx": "^1.2.1", - "dotenv": "^16.3.1", + "dotenv": "^16.4.4", "express": "^4.18.2", "express-validator": "^7.0.1", "firebase": "^10.1.0", @@ -34,10 +34,14 @@ "prism": "^4.1.2", "react": "18.2.0", "react-dom": "18.2.0", + "react-hot-toast": "^2.4.1", + "react-modal": "^3.16.1", "react-router-dom": "^6.11.1", + "react-tooltip": "^5.26.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.0", - "tslib": "^2.3.0" + "tslib": "^2.3.0", + "uuid": "^9.0.1" }, "devDependencies": { "@babel/preset-react": "^7.14.5", @@ -61,11 +65,13 @@ "@types/node": "18.14.2", "@types/react": "18.0.28", "@types/react-dom": "18.0.11", + "@types/react-modal": "^3.16.3", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.58.0", "@typescript-eslint/parser": "^5.58.0", "autoprefixer": "^10.4.14", "babel-jest": "^29.4.1", - "cypress": "^12.11.0", + "cypress": "^12.17.4", "cypress-react-app-actions": "^1.0.2", "eslint": "~8.15.0", "eslint-config-prettier": "8.1.0", @@ -2029,6 +2035,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/@cypress/request/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@cypress/webpack-dev-server": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/@cypress/webpack-dev-server/-/webpack-dev-server-3.6.1.tgz", @@ -2839,6 +2854,28 @@ "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.10.1.tgz", "integrity": "sha512-Dq5rYfEpdeel0bLVN+nfD1VWmzCkK+pJbSjIawGE+RY4+NIJqhbUDDQjvV0NUK84fMfwxvtFoCtEe70HfZjFcw==" }, + "node_modules/@floating-ui/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", + "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "dependencies": { + "@floating-ui/utils": "^0.2.1" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", + "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + }, "node_modules/@google-cloud/firestore": { "version": "6.8.0", "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-6.8.0.tgz", @@ -2944,6 +2981,15 @@ "node": ">=12" } }, + "node_modules/@google-cloud/storage/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "optional": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@grpc/grpc-js": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.7.3.tgz", @@ -5176,6 +5222,17 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/@nx/nest/node_modules/dotenv": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz", + "integrity": "sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, "node_modules/@nx/nest/node_modules/fs-extra": { "version": "11.1.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", @@ -7392,6 +7449,15 @@ "@types/react": "*" } }, + "node_modules/@types/react-modal": { + "version": "3.16.3", + "resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.16.3.tgz", + "integrity": "sha512-xXuGavyEGaFQDgBv4UVm8/ZsG+qxeQ7f77yNrW3n+1J6XAstUy5rYHeIHPh1KzsGc6IkCIdu6lQ2xWzu1jBTLg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.2.tgz", @@ -7512,6 +7578,12 @@ "node": ">=0.10.0" } }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", + "dev": true + }, "node_modules/@types/webpack": { "version": "4.41.35", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.35.tgz", @@ -8594,11 +8666,11 @@ } }, "node_modules/axios": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz", - "integrity": "sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -9587,6 +9659,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/clean-css": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz", @@ -10640,8 +10717,7 @@ "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/cypress": { "version": "12.17.4", @@ -11377,14 +11453,14 @@ } }, "node_modules/dotenv": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", - "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "version": "16.4.4", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.4.tgz", + "integrity": "sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/dotenv-expand": { @@ -12587,6 +12663,11 @@ "node": ">=4" } }, + "node_modules/exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha512-Z+ktTxTwv9ILfgKCk32OX3n/doe+OcLTRtqK9pcL+JsP3J1/VW8Uvl4ZjLlKqeW4rzK4oesDOGMEMRIZqtP4Iw==" + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -13203,18 +13284,6 @@ "@firebase/util": "1.9.3" } }, - "node_modules/firebase-admin/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/firebase-functions": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/firebase-functions/-/firebase-functions-4.4.1.tgz", @@ -13407,9 +13476,9 @@ "devOptional": true }, "node_modules/follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "funding": [ { "type": "individual", @@ -13997,6 +14066,14 @@ "node": ">=8.6.0" } }, + "node_modules/goober": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz", + "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/google-auth-library": { "version": "8.9.0", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-8.9.0.tgz", @@ -21643,12 +21720,50 @@ "react": "^18.2.0" } }, + "node_modules/react-hot-toast": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", + "dependencies": { + "goober": "^2.1.10" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "node_modules/react-modal": { + "version": "3.16.1", + "resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.16.1.tgz", + "integrity": "sha512-VStHgI3BVcGo7OXczvnJN7yT2TWHJPDXZWyI/a0ssFNhGZWsPmB8cF0z33ewDXq4VfYMO1vXgiv/g8Nj9NDyWg==", + "dependencies": { + "exenv": "^1.2.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.0", + "warning": "^4.0.3" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18", + "react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18" + } + }, "node_modules/react-refresh": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.10.0.tgz", @@ -21721,6 +21836,19 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/react-tooltip": { + "version": "5.26.3", + "resolved": "https://registry.npmjs.org/react-tooltip/-/react-tooltip-5.26.3.tgz", + "integrity": "sha512-MpYAws8CEHUd/RC4GaDCdoceph/T4KHM5vS5Dbk8FOmLMvvIht2ymP2htWdrke7K6lqPO8rz8+bnwWUIXeDlzg==", + "dependencies": { + "@floating-ui/dom": "^1.6.1", + "classnames": "^2.3.0" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -22663,6 +22791,14 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", @@ -23559,19 +23695,6 @@ "node": ">=12" } }, - "node_modules/teeny-request/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "optional": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, "node_modules/terser": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz", @@ -24675,9 +24798,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -24767,6 +24894,14 @@ "makeerror": "1.0.12" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/package.json b/package.json index c290aa6..60fabf9 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "@tailwindcss/typography": "^0.5.9", "axios": "^1.6.0", "clsx": "^1.2.1", - "dotenv": "^16.3.1", + "dotenv": "^16.4.4", "express": "^4.18.2", "express-validator": "^7.0.1", "firebase": "^10.1.0", @@ -42,10 +42,14 @@ "prism": "^4.1.2", "react": "18.2.0", "react-dom": "18.2.0", + "react-hot-toast": "^2.4.1", + "react-modal": "^3.16.1", "react-router-dom": "^6.11.1", + "react-tooltip": "^5.26.3", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.0", - "tslib": "^2.3.0" + "tslib": "^2.3.0", + "uuid": "^9.0.1" }, "devDependencies": { "@babel/preset-react": "^7.14.5", @@ -69,11 +73,13 @@ "@types/node": "18.14.2", "@types/react": "18.0.28", "@types/react-dom": "18.0.11", + "@types/react-modal": "^3.16.3", + "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.58.0", "@typescript-eslint/parser": "^5.58.0", "autoprefixer": "^10.4.14", "babel-jest": "^29.4.1", - "cypress": "^12.11.0", + "cypress": "^12.17.4", "cypress-react-app-actions": "^1.0.2", "eslint": "~8.15.0", "eslint-config-prettier": "8.1.0", diff --git a/tailwind.config.js b/tailwind.config.js index 996becf..b0ce952 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,8 +1,7 @@ const colors = require('tailwindcss/colors'); module.exports = { - purge: ['./apps/frontend/**/*.{js,ts,jsx,tsx}'], - darkMode: false, // or 'media' or 'class' - theme: {}, + content: ['./apps/frontend/**/*.{js,ts,jsx,tsx}'], + media: false, // or 'media' or 'class' variants: {}, plugins: [ require('@tailwindcss/forms'), @@ -10,6 +9,13 @@ module.exports = { require('@tailwindcss/typography'), ], theme: { + screens: { + sm: "640px", + md: "768px", + lg: "1024px", + xl: "1280px", + "2xl": "1536px", + }, colors: { transparent: 'transparent', current: 'currentColor', @@ -89,7 +95,7 @@ module.exports = { 900: '#3e2723', }, gray: colors.gray, - 'blue-gray': colors.blueGray, + 'blue-gray': colors.slate, }, extend: { colors: { @@ -102,4 +108,3 @@ module.exports = { } }, }; -