diff --git a/frontend/index.html b/frontend/index.html index 191149a..e8f3372 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,71 +1,39 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - HKRecruitment - - - -
- - - + + + + + + + + + + + + + + + + + + + + + + + + + + + HKRecruitment + + + + +
+ + + + \ No newline at end of file diff --git a/frontend/public/hkn_logo_white_vector.svg b/frontend/public/hkn_logo_white_vector.svg new file mode 100644 index 0000000..e894e19 --- /dev/null +++ b/frontend/public/hkn_logo_white_vector.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/notfound404.jpg b/frontend/public/notfound404.jpg new file mode 100644 index 0000000..0c08628 Binary files /dev/null and b/frontend/public/notfound404.jpg differ diff --git a/frontend/src/App.css b/frontend/src/App.css index 7c12824..49edeae 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -1,82 +1,13 @@ -.App { - text-align: center; +:root { + --hkblue: #061e33; + --hkwhite: #ffffff; } -.App-logo { - height: 40vmin; - pointer-events: none; +body { + background-color: var(--hkblue); + color: var(--hkwhite); } -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: black; -} -.active { - background: green !important; - color: white !important; -} - -.App-link { - color: #61dafb; -} - -.black { - color: white; - background-color: #282c34; - background: #282c34; -} - -.pink { - color: black; - background-color: pink; - background: pink; -} - -.submitButton { - background-color: #00629b !important; - background: #00629b !important; -} -.myNavbar { - background-color: #f9f9f9; -} -.table { - --bs-table-bg: rgba(55, 81, 113, 0.8) !important ; - --bs-table-accent-bg: transparent; - --bs-table-striped-color: white !important; - --bs-table-striped-bg: rgba(55, 81, 113, 0.8) !important ; - --bs-table-active-color: white !important; - --bs-table-active-bg: rgba(0, 0, 0, 0.1); - --bs-table-hover-color: white !important ; - --bs-table-hover-bg: rgba(0, 0, 0, 0.075); - width: 100%; - margin-bottom: 1rem; - color: white important!; - vertical-align: top; - border-color: #dee2e6; -} - -.table > thead { - vertical-align: bottom; - background-color: rgba(55, 81, 113, 1); -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } +a { + color: var(--hkwhite); } diff --git a/frontend/src/App.css.old b/frontend/src/App.css.old new file mode 100644 index 0000000..7c12824 --- /dev/null +++ b/frontend/src/App.css.old @@ -0,0 +1,82 @@ +.App { + text-align: center; +} + +.App-logo { + height: 40vmin; + pointer-events: none; +} + +@media (prefers-reduced-motion: no-preference) { + .App-logo { + animation: App-logo-spin infinite 20s linear; + } +} + +.App-header { + background-color: #282c34; + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: calc(10px + 2vmin); + color: black; +} +.active { + background: green !important; + color: white !important; +} + +.App-link { + color: #61dafb; +} + +.black { + color: white; + background-color: #282c34; + background: #282c34; +} + +.pink { + color: black; + background-color: pink; + background: pink; +} + +.submitButton { + background-color: #00629b !important; + background: #00629b !important; +} +.myNavbar { + background-color: #f9f9f9; +} +.table { + --bs-table-bg: rgba(55, 81, 113, 0.8) !important ; + --bs-table-accent-bg: transparent; + --bs-table-striped-color: white !important; + --bs-table-striped-bg: rgba(55, 81, 113, 0.8) !important ; + --bs-table-active-color: white !important; + --bs-table-active-bg: rgba(0, 0, 0, 0.1); + --bs-table-hover-color: white !important ; + --bs-table-hover-bg: rgba(0, 0, 0, 0.075); + width: 100%; + margin-bottom: 1rem; + color: white important!; + vertical-align: top; + border-color: #dee2e6; +} + +.table > thead { + vertical-align: bottom; + background-color: rgba(55, 81, 113, 1); +} + +@keyframes App-logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 7c17f28..f602f42 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,70 +1,55 @@ -import "./App.css"; -import "bootstrap/dist/css/bootstrap.min.css"; -import React, { useEffect, useState } from "react"; -import MyNavbar from "./MyNavbar"; -import SignupForm from "./SignupForm"; -import { Route } from "react-router-dom"; -import { Navigate } from "react-router-dom"; -import { Routes } from "react-router-dom"; +import React from "react"; +import { Route, Routes } from "react-router-dom"; import { useAuth0 } from "@auth0/auth0-react"; -import AvaiabilitiesTable from "./AvaiabilitiesTable"; -function App() { - function parseJwt(token) { - return JSON.parse(Buffer.from(token.split(".")[1], "base64").toString()); - } +import Alert from "react-bootstrap/Alert"; - const { - isLoading, - isAuthenticated, - error, - user, - loginWithRedirect, - logout, - getAccessTokenSilently, - } = useAuth0(); +import HKNavbar from "./components/HKNavbar"; +import PageLayout from "./components/PageLayout"; +import LoadingSpinner from "./components/LoadingSpinner"; +import AuthGuard from "./components/AuthGuard"; - const [accessToken, setAccessToken] = useState(""); +import HomePage from "./pages/HomePage"; +import ApplyPage from "./pages/ApplyPage"; +import AvailabilitiesPage from "./pages/AvailabilitiesPage"; +import DebugPage from "./pages/DebugPage"; +import NotFoundPage from "./pages/NotFoundPage"; - useEffect(() => { - if (isAuthenticated) { - getAccessTokenSilently({ - audience: import.meta.env.VITE_AUTH0_AUDIENCE, - grant_type: "client_credentials", - }).then((token) => { - setAccessToken(parseJwt(token)); - }); - } - }, [isAuthenticated]); +import "./App.css"; - return ( - - - } - /> - } /> - - ); -} -export default App; +function App() { + const { isLoading } = useAuth0(); -function AfterLogin(props) { return ( -
- {" "} - - {props.isAuthenticated && - !props.user.email.endsWith("@hknpolito.org") && } - {props.isAuthenticated && props.user.email.endsWith("@hknpolito.org") && ( - - )} -
+ <> + + + + Note: all of this is work-in-progress, stuff will change! + + {isLoading ? ( + + ) : ( + + } /> + } + /> + } + /> + } + /> + } /> + + )} + + ); } + +export default App; diff --git a/frontend/src/Auth0ProviderWithNavigate.jsx b/frontend/src/Auth0ProviderWithNavigate.jsx new file mode 100644 index 0000000..24ab643 --- /dev/null +++ b/frontend/src/Auth0ProviderWithNavigate.jsx @@ -0,0 +1,32 @@ +import { Auth0Provider } from "@auth0/auth0-react"; +import React from "react"; +import { useNavigate } from "react-router-dom"; + +export const Auth0ProviderWithNavigate = ({ children }) => { + const navigate = useNavigate(); + + const domain = import.meta.env.VITE_AUTH0_DOMAIN; + const clientId = import.meta.env.VITE_AUTH0_CLIENTID; + const audience = import.meta.env.VITE_AUTH0_AUDIENCE; + const redirectUri = window.location.origin; + + const onRedirectCallback = (appState) => { + navigate(appState?.returnTo || window.location.pathname); + }; + + if (!(domain && clientId && redirectUri)) { + return null; + } + + return ( + + {children} + + ); +}; diff --git a/frontend/src/LoginButton.tsx b/frontend/src/LoginButton.tsx deleted file mode 100644 index be1df82..0000000 --- a/frontend/src/LoginButton.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from "react"; -import { useAuth0 } from "@auth0/auth0-react"; -import Button from "react-bootstrap/Button"; - -const LoginButton = () => { - const { isAuthenticated, loginWithRedirect, logout } = useAuth0(); - - return ( - - ); -}; - -export default LoginButton; diff --git a/frontend/src/MyNavbar.jsx b/frontend/src/MyNavbar.jsx deleted file mode 100644 index 44bc283..0000000 --- a/frontend/src/MyNavbar.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { ReactPropTypes } from "react"; -import Navbar from "react-bootstrap/Navbar"; -import Container from "react-bootstrap/Container"; -import Row from "react-bootstrap/Row"; -import LoginButton from "./LoginButton"; - -import Col from "react-bootstrap/Col"; -import { useAuth0 } from "@auth0/auth0-react"; - -const MyNavbar = (props) => { - const { isLoading, isAuthenticated, error, user, loginWithRedirect, logout } = - useAuth0(); - - return ( - - - - - - {" "} - HKRecruitment - - - - {" "} -

- {isAuthenticated - ? "Hello " + user.name.split(" ")[0] + "!" - : "Login"} -

- - - - -
-
-
- ); -}; - -export default MyNavbar; diff --git a/frontend/src/SignupForm.jsx b/frontend/src/components/ApplyForm.jsx.old similarity index 100% rename from frontend/src/SignupForm.jsx rename to frontend/src/components/ApplyForm.jsx.old diff --git a/frontend/src/components/AuthGuard.jsx b/frontend/src/components/AuthGuard.jsx new file mode 100644 index 0000000..4034e33 --- /dev/null +++ b/frontend/src/components/AuthGuard.jsx @@ -0,0 +1,17 @@ +import { withAuthenticationRequired } from "@auth0/auth0-react"; +import React from "react"; +import LoadingSpinner from "./LoadingSpinner"; + +const AuthGuard = ({ component }) => { + const Component = withAuthenticationRequired(component, { + onRedirecting: () => ( +
+ +
+ ), + }); + + return ; +}; + +export default AuthGuard; diff --git a/frontend/src/AvaiabilitiesCell.jsx b/frontend/src/components/AvailabilitiesCell.jsx similarity index 100% rename from frontend/src/AvaiabilitiesCell.jsx rename to frontend/src/components/AvailabilitiesCell.jsx diff --git a/frontend/src/AvaiabilitiesTable.tsx b/frontend/src/components/AvailabilitiesTable.jsx similarity index 94% rename from frontend/src/AvaiabilitiesTable.tsx rename to frontend/src/components/AvailabilitiesTable.jsx index 712b46b..fff90b3 100644 --- a/frontend/src/AvaiabilitiesTable.tsx +++ b/frontend/src/components/AvailabilitiesTable.jsx @@ -2,12 +2,12 @@ import Table from "react-bootstrap/Table"; import Container from "react-bootstrap/Container"; import Row from "react-bootstrap/Row"; import Col from "react-bootstrap/Col"; -import AvaiabilitiesCell from "./AvaiabilitiesCell"; +import AvaiabilitiesCell from "./AvailabilitiesCell"; import moment from "moment"; import { useState, useEffect } from "react"; import { createUserSchema } from "@hkrecruitment/shared"; import React from "react"; -import { getApplicants, getUsers, getInterviewsByDates } from "./ApiRequests"; +// import { getApplicants, getUsers, getInterviewsByDates } from "../services/API"; function AvaiabilitiesTable(props) { //const start = "2014-09-08T08:00:00"; @@ -29,13 +29,13 @@ function AvaiabilitiesTable(props) { // Suppongo che interviews restituisca un array di colloqui fissati in un certo periodo, ciascuno con data e ora const [interviews, setInterviews] = useState(null); - useEffect(() => { - if (startDate !== null) { - setInterviews( - getInterviewsByDates(startDate.getDate(), startDate.getDate() + 7) - ); - } - }, [startDate]); + // useEffect(() => { + // if (startDate !== null) { + // setInterviews( + // getInterviewsByDates(startDate.getDate(), startDate.getDate() + 7) + // ); + // } + // }, [startDate]); // Crea una matrice 16x7 a partire dalle interviste let fill = [[], []]; diff --git a/frontend/src/components/HKNavbar.jsx b/frontend/src/components/HKNavbar.jsx new file mode 100644 index 0000000..2e63ebe --- /dev/null +++ b/frontend/src/components/HKNavbar.jsx @@ -0,0 +1,74 @@ +import React from "react"; +import { useAuth0 } from "@auth0/auth0-react"; + +import Navbar from "react-bootstrap/Navbar"; +import Container from "react-bootstrap/Container"; +import Row from "react-bootstrap/Row"; +import Image from "react-bootstrap/Image"; +import { Link } from "react-router-dom"; +import Col from "react-bootstrap/Col"; +import NavDropdown from "react-bootstrap/NavDropdown"; + +import LogoutButton from "./LogoutButton"; +import LoginButton from "./LoginButton"; +import LoadingSpinner from "./LoadingSpinner"; + +const HKNavbar = () => { + const { isLoading, isAuthenticated, user } = useAuth0(); + + return ( + + + + + + + Home + + + + + +

HKRecruitment

+ + + + {isLoading ? ( + + ) : !isAuthenticated ? ( + + ) : ( + <> + + } + > + {user.name} + + + + + )} + +
+
+
+ ); +}; + +export default HKNavbar; diff --git a/frontend/src/components/HomeButton.jsx b/frontend/src/components/HomeButton.jsx new file mode 100644 index 0000000..008af93 --- /dev/null +++ b/frontend/src/components/HomeButton.jsx @@ -0,0 +1,15 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { Button } from "react-bootstrap"; + +const HomeButton = () => { + return ( + + + + ); +}; + +export default HomeButton; diff --git a/frontend/src/components/LoadingSpinner.jsx b/frontend/src/components/LoadingSpinner.jsx new file mode 100644 index 0000000..0a73893 --- /dev/null +++ b/frontend/src/components/LoadingSpinner.jsx @@ -0,0 +1,11 @@ +import { Spinner } from "react-bootstrap"; + +const LoadingSpinner = () => { + return ( +
+ +
+ ); +}; + +export default LoadingSpinner; diff --git a/frontend/src/components/LoginButton.jsx b/frontend/src/components/LoginButton.jsx new file mode 100644 index 0000000..2014a16 --- /dev/null +++ b/frontend/src/components/LoginButton.jsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Button } from "react-bootstrap"; +import { useAuth0 } from "@auth0/auth0-react"; + +const LoginButton = () => { + const { loginWithRedirect } = useAuth0(); + + const handleLogin = async () => { + await loginWithRedirect({ + appState: { + returnTo: window.location.pathname, + }, + }); + }; + + return ( + + ); +}; + +export default LoginButton; diff --git a/frontend/src/components/LogoutButton.jsx b/frontend/src/components/LogoutButton.jsx new file mode 100644 index 0000000..7d5ca55 --- /dev/null +++ b/frontend/src/components/LogoutButton.jsx @@ -0,0 +1,19 @@ +import React from "react"; +import { Button } from "react-bootstrap"; +import { useAuth0 } from "@auth0/auth0-react"; + +const LogoutButton = () => { + const { logout } = useAuth0(); + + const handleLogout = () => { + logout({ + logoutParams: { + returnTo: window.location.origin, + }, + }); + }; + + return ; +}; + +export default LogoutButton; diff --git a/frontend/src/components/PageHeader.jsx b/frontend/src/components/PageHeader.jsx new file mode 100644 index 0000000..84c8e64 --- /dev/null +++ b/frontend/src/components/PageHeader.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import { Container } from "react-bootstrap"; +import HomeButton from "./HomeButton"; + +const PageHeader = ({ children }) => { + return ( + + +

{children}

+
+ ); +}; + +export default PageHeader; diff --git a/frontend/src/components/PageLayout.jsx b/frontend/src/components/PageLayout.jsx new file mode 100644 index 0000000..c31625a --- /dev/null +++ b/frontend/src/components/PageLayout.jsx @@ -0,0 +1,17 @@ +import React from "react"; +import { Container, Row, Col } from "react-bootstrap"; + +const PageLayout = ({ children }) => { + return ( + + + + {/* Main content column (takes 8 columns on medium-sized screens and larger) */} + {children} + + + + ); +}; + +export default PageLayout; diff --git a/frontend/src/index.css b/frontend/src/index.css index 7183f7b..4a1df4d 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -11,7 +11,3 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; } - -.active { - color: green; -} diff --git a/frontend/src/index.jsx b/frontend/src/index.jsx new file mode 100644 index 0000000..c0dee18 --- /dev/null +++ b/frontend/src/index.jsx @@ -0,0 +1,17 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; +import { BrowserRouter } from "react-router-dom"; +import { Auth0ProviderWithNavigate } from "./Auth0ProviderWithNavigate"; +import "./index.css"; +import "bootstrap/dist/css/bootstrap.min.css"; +import App from "./App"; + +ReactDOM.createRoot(document.getElementById("root")).render( + + + + + + + +); diff --git a/frontend/src/logo.svg b/frontend/src/logo.svg deleted file mode 100644 index 9dfc1c0..0000000 --- a/frontend/src/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx deleted file mode 100644 index 3a52bed..0000000 --- a/frontend/src/main.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; -import ReactDOM from "react-dom/client"; -import "./index.css"; -import App from "./App"; -import { BrowserRouter } from "react-router-dom"; -import { Auth0Provider } from "@auth0/auth0-react"; - -ReactDOM.createRoot(document.getElementById("root")).render( - - - - - - - -); diff --git a/frontend/src/pages/ApplyPage.jsx b/frontend/src/pages/ApplyPage.jsx new file mode 100644 index 0000000..a957be8 --- /dev/null +++ b/frontend/src/pages/ApplyPage.jsx @@ -0,0 +1,13 @@ +import React from "react"; +import PageHeader from "../components/PageHeader"; + +function ApplyPage() { + return ( + <> + Apply +

This page will be used by non-members to apply.

+ + ); +} + +export default ApplyPage; diff --git a/frontend/src/pages/AvailabilitiesPage.jsx b/frontend/src/pages/AvailabilitiesPage.jsx new file mode 100644 index 0000000..6c97699 --- /dev/null +++ b/frontend/src/pages/AvailabilitiesPage.jsx @@ -0,0 +1,32 @@ +import React, { useEffect, useState } from "react"; +import { useAuth0 } from "@auth0/auth0-react"; +import AvailabilitiesTable from "../components/AvailabilitiesTable"; +import PageHeader from "../components/PageHeader"; + +function AvailabilitiesPage() { + const { isAuthenticated, getAccessTokenSilently } = useAuth0(); + const [accessToken, setAccessToken] = useState(""); + + /* AUTH */ + useEffect(() => { + if (isAuthenticated) { + getAccessTokenSilently({ + audience: import.meta.env.VITE_AUTH0_AUDIENCE, + grant_type: "client_credentials", + }).then((token) => { + setAccessToken(token); + }); + } + }, [isAuthenticated]); + + return ( + <> + Availabilities +

This page will be used by members to manage their availabilities.

+ + + + ); +} + +export default AvailabilitiesPage; diff --git a/frontend/src/pages/DebugPage.jsx b/frontend/src/pages/DebugPage.jsx new file mode 100644 index 0000000..6012989 --- /dev/null +++ b/frontend/src/pages/DebugPage.jsx @@ -0,0 +1,41 @@ +import React, { useEffect, useState } from "react"; +import PageHeader from "../components/PageHeader"; +import { useAuth0 } from "@auth0/auth0-react"; +import Accordion from "react-bootstrap/Accordion"; + +function DebugPage() { + const { isAuthenticated, user, getAccessTokenSilently } = useAuth0(); + const [accessToken, setAccessToken] = useState(""); + + /* AUTH */ + useEffect(() => { + if (isAuthenticated) { + getAccessTokenSilently({ + audience: import.meta.env.VITE_AUTH0_AUDIENCE, + grant_type: "client_credentials", + }).then((token) => { + setAccessToken(token); + }); + } + }, [isAuthenticated]); + + return ( + <> + Debug + + + User info + +
{JSON.stringify(user, null, 2)}
+
+
+ + Access Token + {accessToken} + +
+ + ); +} + +export default DebugPage; diff --git a/frontend/src/pages/HomePage.jsx b/frontend/src/pages/HomePage.jsx new file mode 100644 index 0000000..da2da34 --- /dev/null +++ b/frontend/src/pages/HomePage.jsx @@ -0,0 +1,36 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import { useAuth0 } from "@auth0/auth0-react"; + +function HomePage() { + const { isAuthenticated, user } = useAuth0(); + + return ( + <> + {isAuthenticated ? ( +

Welcome, {user.given_name}.

+ ) : ( +

Welcome, anonymous.

+ )} +

This is the HKN Polito Recruitment Platform.

+ + {isAuthenticated ? ( + <> +
+

Available pages:

+ Apply +
+ Availabilities +
+ Debug + + ) : ( + <> +

To get started, please login.

+ + )} + + ); +} + +export default HomePage; diff --git a/frontend/src/pages/NotFoundPage.jsx b/frontend/src/pages/NotFoundPage.jsx new file mode 100644 index 0000000..266765b --- /dev/null +++ b/frontend/src/pages/NotFoundPage.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import Image from "react-bootstrap/Image"; +import PageHeader from "../components/PageHeader"; + +function NotFoundPage() { + return ( + <> + 404 - Page not found + + + ); +} + +export default NotFoundPage; diff --git a/frontend/src/ApiRequests.tsx b/frontend/src/services/API.ts similarity index 100% rename from frontend/src/ApiRequests.tsx rename to frontend/src/services/API.ts