Skip to content
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

functional profile designer #24

Merged
merged 19 commits into from
Jul 13, 2024
Merged
1 change: 1 addition & 0 deletions clientv2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@kinde-oss/kinde-auth-react": "^4.0.1",
"@mantine/core": "7.11.1",
"@mantine/hooks": "7.11.1",
"@mantine/notifications": "^7.11.1",
"@tabler/icons-react": "^3.10.0",
"axios": "^1.7.2",
"dotenv": "^16.4.5",
Expand Down
3 changes: 3 additions & 0 deletions clientv2/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import '@mantine/core/styles.css';
import '@mantine/notifications/styles.css';
import { MantineProvider } from '@mantine/core';
import { Notifications } from '@mantine/notifications';
import { Router } from './Router';
import { theme } from './theme';

export default function App() {
return (
<MantineProvider theme={theme}>
<Notifications position="top-right" />
<Router />
</MantineProvider>
);
Expand Down
12 changes: 10 additions & 2 deletions clientv2/src/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createBrowserRouter, Navigate, RouterProvider } from 'react-router-dom';
import { AuthRoleEnum, useAuth } from './components/AuthContext';
import { LoginPage } from './pages/Login.page';
import { StudentHomePage } from './pages/student/StudentHome.page';
import { StudentHomePage } from './pages/student/StudentProfile.page';
import { RecruiterHomePage } from './pages/recruiter/RecruiterHome.page';

const unauthenticatedRouter = createBrowserRouter([
Expand All @@ -13,6 +13,10 @@ const unauthenticatedRouter = createBrowserRouter([
path: '/login',
element: <LoginPage />,
},
{
path: '/profile',
element: <Navigate to="/login" replace />,
},
]);

const recruiterRouter = createBrowserRouter([
Expand All @@ -29,12 +33,16 @@ const recruiterRouter = createBrowserRouter([
const studentRouter = createBrowserRouter([
{
path: '/',
element: <StudentHomePage />,
element: <Navigate to="/profile" replace />,
},
{
path: '/login',
element: <Navigate to="/" replace />,
},
{
path: '/profile',
element: <StudentHomePage />,
},
]);

export function Router() {
Expand Down
32 changes: 18 additions & 14 deletions clientv2/src/components/AuthContext/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React, { createContext, ReactNode, useContext, useState, useEffect } from 'react';
import { useMsal } from '@azure/msal-react';
import { useKindeAuth } from '@kinde-oss/kinde-auth-react';
import { AuthenticationResult, InteractionRequiredAuthError, InteractionStatus } from '@azure/msal-browser';
import {
AuthenticationResult,
InteractionRequiredAuthError,
InteractionStatus,
} from '@azure/msal-browser';
import { MantineProvider } from '@mantine/core';
import FullScreenLoader from './LoadingScreen';

Expand Down Expand Up @@ -57,19 +61,19 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
user,
logout: kindeLogout,
getToken: getKindeToken,
getPermission: getKindePermission
getPermission: getKindePermission,
} = useKindeAuth();

const [userData, setUserData] = useState<AuthContextData | null>(null);
const [isLoggedIn, setIsLoggedIn] = useState<boolean>(
(isAuthenticated || accounts.length > 0) && !isLoading
);
if (isAuthenticated && !isLoading && !userData) {
const isRecruiter = getKindePermission("recruiter:resume-book").isGranted;
const isRecruiter = getKindePermission('recruiter:resume-book').isGranted;
if (!isRecruiter) {
setUserData(null);
setIsLoggedIn(false);
window.location.href = "/";
window.location.href = '/';
} else {
setUserData({
email: user?.email!,
Expand All @@ -79,7 +83,6 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
});
setIsLoggedIn(true);
}

}

useEffect(() => {
Expand All @@ -89,9 +92,10 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
handleMsalResponse(response);
} else if (accounts.length > 0) {
// User is already logged in, set the state
const [lastName, firstName] = accounts[0].name?.split(",")!
setUserData({
email: accounts[0].username,
name: accounts[0].name,
name: `${firstName} ${lastName}`,
authenticationMethod: AuthSourceEnum.MSAL,
role: AuthRoleEnum.STUDENT,
});
Expand Down Expand Up @@ -125,25 +129,25 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
}
if (userData?.authenticationMethod === AuthSourceEnum.MSAL) {
try {
const accounts = instance.getAllAccounts();
if (accounts.length > 0) {
const msalAccounts = instance.getAllAccounts();
if (msalAccounts.length > 0) {
const silentRequest = {
account: accounts[0],
scopes: [".default"], // Adjust scopes as needed
account: msalAccounts[0],
scopes: ['.default'], // Adjust scopes as needed
};
const tokenResponse = await instance.acquireTokenSilent(silentRequest);
return tokenResponse.accessToken;
} else {
throw new Error('No accounts found.');
throw new Error("More than one account found, cannot proceed.")
}
} catch (error) {
console.error('Silent token acquisition failed.', error);
if (error instanceof InteractionRequiredAuthError) {
// Fallback to interaction when silent token acquisition fails
try {
const interactiveRequest = {
scopes: [".default"], // Adjust scopes as needed
redirectUri: "/login", // Redirect URI after login
scopes: ['.default'], // Adjust scopes as needed
redirectUri: '/login', // Redirect URI after login
};
const tokenResponse: any = await instance.acquireTokenRedirect(interactiveRequest);
return tokenResponse.accessToken;
Expand All @@ -161,7 +165,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
}
throw new Error('Unknown authentication method.');
};

const loginMsal = () => {
instance.loginRedirect();
};
Expand Down
26 changes: 26 additions & 0 deletions clientv2/src/components/FullPageError/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { MouseEventHandler } from 'react';
import { Container, Paper, Title, Text, Button } from '@mantine/core';

interface FullPageErrorProps {
errorCode?: number;
errorMessage?: string;
onRetry?: MouseEventHandler<HTMLButtonElement>;
}

const FullPageError: React.FC<FullPageErrorProps> = ({ errorCode, errorMessage, onRetry }) => (
<Container>
<Paper shadow="md" radius="md">
<Title>{errorCode || 'An error occurred'}</Title>
<Text color="dimmed">
{errorMessage || 'Something went wrong. Please try again later.'}
</Text>
{onRetry && (
<Button variant="outline" onClick={onRetry}>
Retry
</Button>
)}
</Paper>
</Container>
);

export default FullPageError;
21 changes: 15 additions & 6 deletions clientv2/src/components/Navbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,25 @@ import { Group, Divider, Box, Burger, Drawer, ScrollArea, rem, Badge } from '@ma
import { useDisclosure } from '@mantine/hooks';
import classes from './index.module.css';
import LogoBadge from './Logo';
import { AuthContextData, AuthSourceEnum } from '../AuthContext';
import { AuthContextData, AuthRoleEnum, AuthSourceEnum } from '../AuthContext';
import { AuthenticatedProfileDropdown } from '../ProfileDropdown';

interface HeaderNavbarProps {
userData?: AuthContextData | null;
}

const isActiveLink = (path: string) => location.pathname === path;

const HeaderNavbar: React.FC<HeaderNavbarProps> = ({ userData }) => {
const [drawerOpened, { toggle: toggleDrawer, close: closeDrawer }] = useDisclosure(false);
let badge = null;
if (userData?.authenticationMethod === AuthSourceEnum.LOCAL) {
if (userData?.role === AuthRoleEnum.RECRUITER) {
badge = (
<Badge color="blue" style={{ marginLeft: 10 }}>
Recruiter
</Badge>
);
} else if (userData?.authenticationMethod === AuthSourceEnum.MSAL) {
} else if (userData?.role === AuthRoleEnum.STUDENT) {
badge = (
<Badge color="#FF5F05" style={{ marginLeft: 10 }}>
Student
Expand All @@ -34,9 +36,16 @@ const HeaderNavbar: React.FC<HeaderNavbarProps> = ({ userData }) => {
<Group justify="start" h="100%" gap={10}>
<LogoBadge />
{badge}
<a href="/" className={classes.link}>
Home
</a>
{userData?.role !== AuthRoleEnum.STUDENT ? (
<a href="/" className={classes.link}>
Home
</a>
) : null}
{userData?.role === AuthRoleEnum.STUDENT ? (
<a href="/profile" className={classes.link}>
My Profile
</a>
) : null}
</Group>
<Group h="100%" justify="end" gap={10} visibleFrom="sm">
{userData ? <AuthenticatedProfileDropdown userData={userData} /> : null}
Expand Down
4 changes: 2 additions & 2 deletions clientv2/src/components/ProfileDropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const AuthenticatedProfileDropdown: React.FC<ProfileDropdownProps> = ({ userData
>
<Center inline>
<Box component="span" mr={5}>
Profile
My Account
</Box>
<IconChevronDown
style={{ width: rem(16), height: rem(16) }}
Expand All @@ -57,7 +57,7 @@ const AuthenticatedProfileDropdown: React.FC<ProfileDropdownProps> = ({ userData
</a>
</Popover.Target>

<Popover.Dropdown style={{ overflow: 'hidden' }} aria-label="Authenticated Profile Dropdown">
<Popover.Dropdown style={{ overflow: 'hidden' }} aria-label="Authenticated My Account Dropdown">
<SimpleGrid cols={1} spacing={0}>
<UnstyledButton className={classes.subLink} key="name">
<Group wrap="nowrap" align="flex-start">
Expand Down
Loading
Loading