Skip to content

MVP of Auth0 signup flow, additional signup form, dashboard access (styling per Figma design). #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ dist-ssr
.amplify
amplify_outputs*
amplifyconfiguration*

# dotenv files
.env
.env.*
4 changes: 2 additions & 2 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/png" href="/helpful-logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Dashboard - Helpful Engineering</title>
</head>
<body>
<div id="root"></div>
Expand Down
841 changes: 787 additions & 54 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@
"test": "vitest run --passWithNoTests"
},
"dependencies": {
"@auth0/auth0-react": "^2.3.0",
"@aws-amplify/ui-react": "^6.5.5",
"@types/react-bootstrap": "^0.32.37",
"@types/react-modal": "^3.16.3",
"@types/react-router-dom": "^5.3.3",
"aws-amplify": "^6.6.6",
"bootstrap": "^5.3.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-bootstrap": "^2.10.9",
"react-dom": "^18.2.0",
"react-modal": "^3.16.3",
"react-router-dom": "^7.5.3",
"react-select": "^5.10.1"
},
"devDependencies": {
"@aws-amplify/backend": "^1.5.0",
Expand Down
Binary file added public/helpful-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion public/vite.svg

This file was deleted.

1 change: 0 additions & 1 deletion src/assets/react.svg

This file was deleted.

10 changes: 10 additions & 0 deletions src/auth0/login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useAuth0 } from "@auth0/auth0-react";
import React from "react";

const LoginButton: React.FC = () => {
const { loginWithRedirect } = useAuth0();

return <button onClick={() => loginWithRedirect()} className="login-button">Log In</button>;
};

export default LoginButton;
29 changes: 29 additions & 0 deletions src/auth0/profile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useAuth0 } from "@auth0/auth0-react";
import React from "react";

interface User {
picture?: string;
name?: string;
email?: string;
}

const Profile: React.FC = () => {
const { user, isAuthenticated, isLoading } = useAuth0<User>();

if (isLoading) {
return <div>Loading ...</div>;
}

return (
isAuthenticated &&
user && (
<div>
<img src={user.picture} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
)
);
};

export default Profile;
18 changes: 18 additions & 0 deletions src/auth0/signup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useAuth0 } from "@auth0/auth0-react";

const SignupButton = () => {
const { loginWithRedirect } = useAuth0();

const handleSignup = () => {
loginWithRedirect({
authorizationParams: {
screen_hint: "signup",
},
appState: { returnTo: "/signup" }
});
};

return <button onClick={handleSignup} className="signinup-button">Sign In/Up&nbsp;▼</button>;
};

export default SignupButton;
16 changes: 16 additions & 0 deletions src/components/BackNavigationFix.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// This function is used to fix the back navigation issue after signup where navigating back in history can show a stale or invalid Auth0 transaction page, leading to errors. This is a known issue when using auth0 and SPA. Need further testing as it doesn't seem to solve this problem.

import { useEffect } from "react";

export default function BackNavigationFix() {
useEffect(() => {
const handlePageShow = (event: PageTransitionEvent) => {
if (event.persisted) {
window.location.reload();
}
};
window.addEventListener("pageshow", handlePageShow);
return () => window.removeEventListener("pageshow", handlePageShow);
}, []);
return null;
}
2 changes: 1 addition & 1 deletion src/components/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import './Card.css';
import './styles/Card.css';

interface CardProps {
title: string;
Expand Down
12 changes: 8 additions & 4 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React from 'react';
import './Header.css';
import './styles/Header.css';
import helpfulIcon from '../assets/helpful_icon.png';
import profileIcon from '../assets/profile_icon.png';
// will need to work on the transition between login and signup, dynamic change to header buttons based on logged in status this was a work around for now to have a blank page (using the component: PublicHeader.tsx) with a button that has auth0 functionality to simulate a signup coming in from the public WP site. For the sake of time, it was easier to wait and fix this later. (Eric)
import LoginButton from "../auth0/login";
// import LogoutButton from "../auth0/logout"; <--- logout button is implemented in auth0 folder, just need time to sort out the logged in view of header, add the button and test it.
import SignupButton from "../auth0/signup";

const Header: React.FC = () => {
return (
Expand All @@ -17,13 +21,13 @@ const Header: React.FC = () => {
<a href="#contact">Contact</a>
</div>
<div className="nav-buttons">
<button className="login-button">Login</button>
<button className="signup-button">Sign Up</button>
<LoginButton/>
<SignupButton/>
<img src={profileIcon} alt="Profile Icon" className="profile-icon" />
</div>
</nav>
</header>
);
};

export default Header;
export default Header;
32 changes: 32 additions & 0 deletions src/components/PublicHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import "./styles/PublicHeader.css";
import helpfulIcon from "../assets/helpful_icon.png";
import SignupButton from "../auth0/signup";

const PublicHeader: React.FC = () => {
return (
<header className="header-container">
<nav className="header-nav">
<div className="logo-section">
<img src={helpfulIcon} alt="Helpful" className="helpful-icon" />
<a href="/" className="logo-text">
HELPFUL
</a>
</div>
<div className="nav-links">
<a href="#about">About</a>
<a href="#partners">Partners</a>
<a href="#research">Research</a>
<a href="#events">Events</a>
<a href="#blog">Blog</a>
<a href="#contact">Contact</a>
</div>
<div className="nav-buttons">
<button className="donate-button">DONATE</button>
<SignupButton />
</div>
</nav>
</header>
);
};

export default PublicHeader;
85 changes: 85 additions & 0 deletions src/components/RouteGuards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useAuth0, Auth0Provider } from "@auth0/auth0-react";
import { Navigate, useLocation, useSearchParams } from "react-router-dom";
import Dashboard from "../pages/Dashboard";
import SignupPage from "../pages/SignupPage";
import { getUserData } from '../utils/mockDB';

// Protects the dashboard route: only accessible if authenticated AND signup is complete
export function ProtectedDashboard() {
const { isAuthenticated, isLoading, user } = useAuth0();
const location = useLocation();

if (isLoading) return null;

// If not authenticated or no user ID, redirect to home
if (!isAuthenticated || !user?.sub) {
return <Navigate to="/" replace />;
}

// Check if signup is complete using mockDB. Will need to hook up to our preferred DB for production
const userData = getUserData(user.sub);

if (!userData) {
// If signup not complete, redirect to /signup
return <Navigate to="/signup" state={{ from: location }} replace />;
}

return <Dashboard />;
}

// Protects the signup route: only accessible if authenticated and signup is NOT complete
export function ProtectedSignup() {
const { isAuthenticated, isLoading, user, loginWithRedirect } = useAuth0();
const location = useLocation();
const [searchParams] = useSearchParams();

// Show Auth0 errors if present in the URL
const auth0Error = searchParams.get("error");
const auth0ErrorDescription = searchParams.get("error_description");
if (auth0Error) {
return (
<div>
<h2>Authentication Error</h2>
<p>{auth0Error}: {auth0ErrorDescription}</p>
</div>
);
}

if (isLoading) return null;

// If not authenticated, trigger Auth0 login and redirect back to /signup after login
if (!isAuthenticated) {
loginWithRedirect({ appState: { returnTo: "/signup" } });
return null;
}

// If signup is already complete, redirect to dashboard
if (user?.sub && getUserData(user.sub)) {
return <Navigate to="/dashboard" state={{ from: location }} replace />;
}

return <SignupPage />;
}

// Auth0Provider wrapper
interface Auth0ProviderWithRedirectProps {
children: React.ReactNode;
}

export function Auth0ProviderWithRedirect({ children }: Auth0ProviderWithRedirectProps) {
const domain = import.meta.env.VITE_AUTH0_DOMAIN;
const clientId = import.meta.env.VITE_AUTH0_CLIENT_ID;
const redirectUri = `${window.location.origin}/`;

return (
<Auth0Provider
domain={domain}
clientId={clientId}
authorizationParams={{
redirect_uri: redirectUri,
}}
>
{children}
</Auth0Provider>
);
}
Loading