diff --git a/GomezMorinFrontEnd/package-lock.json b/GomezMorinFrontEnd/package-lock.json index 620349f..21ffa6f 100644 --- a/GomezMorinFrontEnd/package-lock.json +++ b/GomezMorinFrontEnd/package-lock.json @@ -8,14 +8,19 @@ "name": "gomezmorinfrontend", "version": "0.0.0", "dependencies": { + "@headlessui/react": "^1.7.14", + "@heroicons/react": "^2.0.17", "@reduxjs/toolkit": "^1.9.3", "axios": "^1.3.4", "google-map-react": "^2.2.0", "google-maps-react": "^2.0.6", + "headlessui": "^0.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.43.8", - "react-redux": "^8.0.5" + "react-icons": "^4.8.0", + "react-redux": "^8.0.5", + "react-router-dom": "^6.10.0" }, "devDependencies": { "@types/react": "^18.0.28", @@ -754,6 +759,29 @@ "fast-deep-equal": "^3.1.3" } }, + "node_modules/@headlessui/react": { + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.14.tgz", + "integrity": "sha512-znzdq9PG8rkwcu9oQ2FwIy0ZFtP9Z7ycS+BAqJ3R5EIqC/0bJGvhT7193rFf+45i9nnPsYvCQVW4V/bB9Xc+gA==", + "dependencies": { + "client-only": "^0.0.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, + "node_modules/@heroicons/react": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.17.tgz", + "integrity": "sha512-90GMZktkA53YbNzHp6asVEDevUQCMtxWH+2UK2S8OpnLEu7qckTJPhNxNQG52xIR1WFTwFqtH6bt7a60ZNcLLA==", + "peerDependencies": { + "react": ">= 16" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -864,6 +892,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz", + "integrity": "sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==", + "engines": { + "node": ">=14" + } + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -1164,6 +1200,11 @@ "node": ">= 6" } }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1539,6 +1580,11 @@ "node": ">=4" } }, + "node_modules/headlessui": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/headlessui/-/headlessui-0.0.0.tgz", + "integrity": "sha512-CHvacVPbl8AqIg2sBNKySUmumu7o15jSrCaTrIh9GW2Eq4y/krCN/vZFOsKCwlrhWQbO4267a8xvvP8bs+qREQ==" + }, "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", @@ -2050,6 +2096,14 @@ "react": "^16.8.0 || ^17 || ^18" } }, + "node_modules/react-icons": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.8.0.tgz", + "integrity": "sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -2102,6 +2156,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.10.0.tgz", + "integrity": "sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==", + "dependencies": { + "@remix-run/router": "1.5.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.10.0.tgz", + "integrity": "sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==", + "dependencies": { + "@remix-run/router": "1.5.0", + "react-router": "6.10.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -2882,6 +2966,20 @@ "fast-deep-equal": "^3.1.3" } }, + "@headlessui/react": { + "version": "1.7.14", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.14.tgz", + "integrity": "sha512-znzdq9PG8rkwcu9oQ2FwIy0ZFtP9Z7ycS+BAqJ3R5EIqC/0bJGvhT7193rFf+45i9nnPsYvCQVW4V/bB9Xc+gA==", + "requires": { + "client-only": "^0.0.1" + } + }, + "@heroicons/react": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.0.17.tgz", + "integrity": "sha512-90GMZktkA53YbNzHp6asVEDevUQCMtxWH+2UK2S8OpnLEu7qckTJPhNxNQG52xIR1WFTwFqtH6bt7a60ZNcLLA==", + "requires": {} + }, "@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -2962,6 +3060,11 @@ "reselect": "^4.1.7" } }, + "@remix-run/router": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.5.0.tgz", + "integrity": "sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==" + }, "@types/hoist-non-react-statics": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", @@ -3172,6 +3275,11 @@ } } }, + "client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3442,6 +3550,11 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, + "headlessui": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/headlessui/-/headlessui-0.0.0.tgz", + "integrity": "sha512-CHvacVPbl8AqIg2sBNKySUmumu7o15jSrCaTrIh9GW2Eq4y/krCN/vZFOsKCwlrhWQbO4267a8xvvP8bs+qREQ==" + }, "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", @@ -3773,6 +3886,12 @@ "integrity": "sha512-BQm+Ge5KjTk1EchDBRhdP8Pkb7MArO2jFF+UWYr3rtvh6197khi22uloLqlWeuY02ItlCzPunPsFt1/q9wQKnw==", "requires": {} }, + "react-icons": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.8.0.tgz", + "integrity": "sha512-N6+kOLcihDiAnj5Czu637waJqSnwlMNROzVZMhfX68V/9bu9qHaMIJC4UdozWoOk57gahFCNHwVvWzm0MTzRjg==", + "requires": {} + }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -3797,6 +3916,23 @@ "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", "dev": true }, + "react-router": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.10.0.tgz", + "integrity": "sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==", + "requires": { + "@remix-run/router": "1.5.0" + } + }, + "react-router-dom": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.10.0.tgz", + "integrity": "sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==", + "requires": { + "@remix-run/router": "1.5.0", + "react-router": "6.10.0" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/GomezMorinFrontEnd/package.json b/GomezMorinFrontEnd/package.json index da82c2b..3dc3acb 100644 --- a/GomezMorinFrontEnd/package.json +++ b/GomezMorinFrontEnd/package.json @@ -9,14 +9,19 @@ "preview": "vite preview" }, "dependencies": { + "@headlessui/react": "^1.7.14", + "@heroicons/react": "^2.0.17", "@reduxjs/toolkit": "^1.9.3", "axios": "^1.3.4", "google-map-react": "^2.2.0", "google-maps-react": "^2.0.6", + "headlessui": "^0.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hook-form": "^7.43.8", - "react-redux": "^8.0.5" + "react-icons": "^4.8.0", + "react-redux": "^8.0.5", + "react-router-dom": "^6.10.0" }, "devDependencies": { "@types/react": "^18.0.28", diff --git a/GomezMorinFrontEnd/public/images/logoMorin.png b/GomezMorinFrontEnd/public/images/logoMorin.png new file mode 100644 index 0000000..3373345 Binary files /dev/null and b/GomezMorinFrontEnd/public/images/logoMorin.png differ diff --git a/GomezMorinFrontEnd/src/App.jsx b/GomezMorinFrontEnd/src/App.jsx index c8a9189..c374f02 100644 --- a/GomezMorinFrontEnd/src/App.jsx +++ b/GomezMorinFrontEnd/src/App.jsx @@ -1,59 +1,19 @@ -import { useDispatch, useSelector } from "react-redux" -import { setUsers } from "./states/generalSlice" -import Card from "./components/Card" -import { useForm } from "react-hook-form" -import { createUser, readUsers } from "./queries/query" -import { useEffect } from "react" -import MapContainer from "./components/MapContainer" - -function App() { - const users = useSelector((state) => state.general.users) - const dispatch = useDispatch() - - const { register, handleSubmit } = useForm() - - const onSubmit = async (data) => { - const response = await createUser(data) - - dispatch(setUsers(response)) - } - - useEffect(() => { - readUsers() - .then((response) => { - - if (response) { - dispatch(setUsers(response)) - } - }) - }, []) +import { BrowserRouter, Route, Routes } from "react-router-dom"; +import Page from "./pages/page"; +import Layout from "./components/Layout"; +const App = () => { return ( -
-
- - - - - -
- -
- -
-
- -
+
+ + + }> + } /> + + +
- ) -} + ); +}; -export default App +export default App; diff --git a/GomezMorinFrontEnd/src/components/Button.jsx b/GomezMorinFrontEnd/src/components/Button.jsx new file mode 100644 index 0000000..5b18a5f --- /dev/null +++ b/GomezMorinFrontEnd/src/components/Button.jsx @@ -0,0 +1,32 @@ +import React from "react"; + +/** + * This is a component for a button. + * + * @param {string} text - The text the button will display. + * @param {string} type - The type of the button. + * @param {string} colorBg - The color of the background of the button. Example: bg-red-600 + * @param {string} colorHoverBg- The color of the background of the button when hover. Example: hover:bg-red-700 + * @param {function} action- The path that will navigate to. + * @returns {JSX.Element} The JSX element displaying a button. + */ + +const Button = ({ text, type, colorBg, colorHoverBg, action }) => { + const styles = [ + "w-full", + colorBg, + "text-white", + "text-sm", + "p-2", + "rounded-lg", + colorHoverBg, + ]; + + return ( + + ); +}; + +export default Button; diff --git a/GomezMorinFrontEnd/src/components/Card.jsx b/GomezMorinFrontEnd/src/components/Card.jsx index ec4458f..f531d03 100644 --- a/GomezMorinFrontEnd/src/components/Card.jsx +++ b/GomezMorinFrontEnd/src/components/Card.jsx @@ -1,35 +1,50 @@ -import React from 'react' -import { useForm } from 'react-hook-form' -import { updateUser, deleteUser } from '../queries/query' -import { setUsers } from '../states/generalSlice' -import { useDispatch } from 'react-redux' +import React from "react"; +import { useForm } from "react-hook-form"; +import { updateUser, deleteUser } from "../queries/query"; +import { setUsers } from "../states/generalSlice"; +import { useDispatch } from "react-redux"; const Card = ({ user }) => { - const { register, handleSubmit } = useForm() - const dispatch = useDispatch() + const { register, handleSubmit } = useForm(); + const dispatch = useDispatch(); const onSubmit = async (data) => { - const response = await updateUser(user._id, data) - - dispatch(setUsers(response)) - } + const response = await updateUser(user._id, data); + + dispatch(setUsers(response)); + }; const deleteQuery = async (id) => { - const response = await deleteUser(id) - console.log(id) + const response = await deleteUser(id); + + dispatch(setUsers(response)); + }; - dispatch(setUsers(response)) - } - return ( -
  • -

    { user.username }

    -
    - - +
  • +

    {user.username}

    + + +
  • - +
  • - ) -} + ); +}; -export default Card \ No newline at end of file +export default Card; diff --git a/GomezMorinFrontEnd/src/components/InputForm.jsx b/GomezMorinFrontEnd/src/components/InputForm.jsx new file mode 100644 index 0000000..9aeb153 --- /dev/null +++ b/GomezMorinFrontEnd/src/components/InputForm.jsx @@ -0,0 +1,36 @@ +import React from "react"; +import { useFormContext } from "react-hook-form"; + +/** + * This is a component for an input in a form using the hook 'Use Form'. + * + * @param {string} label - The text that will appear above the input. + * @param {string} name - The name of the value that will be sent by the form. + * @param {string} type - The type of the input. + * @param {string} placeholder - The text that will be shown on the input space. + * @param {string} defaultValue - The value that will have by default. + * @returns {JSX.Element} The JSX element representing a input form UI. + */ + +const InputForm = ({ label, name, type, placeholder, defaultValue }) => { + const { register } = useFormContext(); + + return ( +
    + + +
    + ); +}; + +export default InputForm; diff --git a/GomezMorinFrontEnd/src/components/Layout.jsx b/GomezMorinFrontEnd/src/components/Layout.jsx new file mode 100644 index 0000000..58e7ac0 --- /dev/null +++ b/GomezMorinFrontEnd/src/components/Layout.jsx @@ -0,0 +1,21 @@ +import React from "react"; +import { Outlet } from "react-router-dom"; +import Navbar from "./Navbar"; + +/** + * This component is used for the single web application. + * In order to show all the components in the routes configured in App.jsx. + * + * @returns {JSX.Element} The JSX element representing the layout UI. + */ + +const Layout = () => { + return ( +
    + + +
    + ); +}; + +export default Layout; diff --git a/GomezMorinFrontEnd/src/components/MapContainer.jsx b/GomezMorinFrontEnd/src/components/MapContainer.jsx deleted file mode 100644 index ccf3a5c..0000000 --- a/GomezMorinFrontEnd/src/components/MapContainer.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React, { Component } from "react"; -import { Map, GoogleApiWrapper, Marker } from "google-maps-react"; - -const apiKey = import.meta.env.VITE_API_KEY_MAPS; -class MapContainer extends Component { - render() { - const mapStyles = { - height: "400px", - }; - - return ( - - - - ); - } -} - -export default GoogleApiWrapper({ - apiKey: apiKey, -})(MapContainer); diff --git a/GomezMorinFrontEnd/src/components/ModalAlert.jsx b/GomezMorinFrontEnd/src/components/ModalAlert.jsx new file mode 100644 index 0000000..d96abf2 --- /dev/null +++ b/GomezMorinFrontEnd/src/components/ModalAlert.jsx @@ -0,0 +1,125 @@ +import { Fragment, React, useState } from "react"; +import { Dialog, Transition } from "@headlessui/react"; +import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; +import Button from "./Button"; + +/** + * This is a component for an alert displayed on a modal giving the user two options. + * + * @param {string} title - The title of the alert. + * @param {string} message - The message that will be displayed on the alert. + * @param {boolean} active - The state to initialize the alert. + * @param {string} buttonName - The name of the button that will trigger the alert. + * @returns {JSX.Element} The JSX element displaying a modal with two buttons. + */ + +const ModalAlert = ({ title, message, active, buttonName }) => { + const [isOpen, setIsOpen] = useState(false); + + const closeModal = () => { + setIsOpen(false); + }; + + const openModal = () => { + setIsOpen(true); + }; + + return ( + <> + + {isOpen ? ( + <> + + + +
    + + +
    +
    + + +
    +
    +
    +
    +
    + + {title} + +
    +

    {message}

    +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    + + ) : null} + + ); +}; + +export default ModalAlert; diff --git a/GomezMorinFrontEnd/src/components/Navbar.jsx b/GomezMorinFrontEnd/src/components/Navbar.jsx new file mode 100644 index 0000000..84fbe82 --- /dev/null +++ b/GomezMorinFrontEnd/src/components/Navbar.jsx @@ -0,0 +1,30 @@ +import React from "react"; +import NavbarItem from "./NavbarItem"; +import NavbarDropdownItem from "./NavbarDropdownItem"; + +/** + * This is the component that will be used as a header of the application. + * + * @returns {JSX.Element} The JSX element representing the navbar UI. + */ + +const Navbar = () => { + return ( + + ); +}; + +export default Navbar; diff --git a/GomezMorinFrontEnd/src/components/NavbarDropdownItem.jsx b/GomezMorinFrontEnd/src/components/NavbarDropdownItem.jsx new file mode 100644 index 0000000..fd65f86 --- /dev/null +++ b/GomezMorinFrontEnd/src/components/NavbarDropdownItem.jsx @@ -0,0 +1,51 @@ +import { Fragment, React } from "react"; +import { Menu, Transition } from "@headlessui/react"; +import ModalAlert from "./ModalAlert"; + +/** + * This is a component for showing a dropdown item in the navbar. + * + * @param {string} userName - The name of the user. + * @returns {JSX.Element} The JSX element displaying a navbar dropdown item. + */ + +const NavbarDropdownItem = ({ userName }) => { + return ( + <> + +
    + + {userName} + +
    + + + +
    + + {({ active }) => ( + + )} + +
    +
    +
    +
    + + ); +}; + +export default NavbarDropdownItem; diff --git a/GomezMorinFrontEnd/src/components/NavbarItem.jsx b/GomezMorinFrontEnd/src/components/NavbarItem.jsx new file mode 100644 index 0000000..302e68f --- /dev/null +++ b/GomezMorinFrontEnd/src/components/NavbarItem.jsx @@ -0,0 +1,34 @@ +import React from "react"; +import { useNavigate } from "react-router-dom"; + +/** + * This is a component for an item of the navbar. + * + * @param {string} children - The html element that will be passed through the component. + * @param {string} navigation - The path that will navigate to. + * @param {boolean} isLoggedOut - The state that confirms or denies if the user is logged out. + * @returns {JSX.Element} The JSX element displaying a navbar item. + */ + +const NavbarItem = ({ children, navigation, isLoggedOut }) => { + const navigate = useNavigate(); + + const currentLink = navigation === window.location.pathname; + + return ( + + ); +}; + +export default NavbarItem;