Skip to content

Commit 257819b

Browse files
Merge branch 'main' into feature/matching-service
2 parents 4cbcdc8 + 402a7ad commit 257819b

32 files changed

+5867
-2035
lines changed

client/package-lock.json

Lines changed: 3016 additions & 1958 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,23 @@
1616
"test:coverage": "jest --config jest.config.cjs --coverage"
1717
},
1818
"dependencies": {
19-
"react": "^19.1.0",
20-
"react-dom": "^19.1.0"
19+
"@auth0/auth0-react": "^2.3.0",
20+
"@emotion/react": "^11.11.3",
21+
"@emotion/styled": "^11.11.0",
22+
"@mui/icons-material": "^5.15.10",
23+
"@mui/material": "^5.15.10",
24+
"@mui/x-date-pickers": "^8.5.2",
25+
"@react-oauth/google": "^0.12.2",
26+
"@types/react-router-dom": "^5.3.3",
27+
"axios": "^1.9.0",
28+
"date-fns": "^4.1.0",
29+
"react": "^18.2.0",
30+
"react-dom": "^18.2.0",
31+
"react-router-dom": "^6.22.1"
2132
},
2233
"devDependencies": {
2334
"@eslint/js": "^9.25.0",
35+
"@tailwindcss/postcss": "^4.1.7",
2436
"@testing-library/jest-dom": "^6.6.3",
2537
"@testing-library/react": "^16.3.0",
2638
"@testing-library/user-event": "^14.6.1",
@@ -30,6 +42,7 @@
3042
"@typescript-eslint/eslint-plugin": "^8.32.1",
3143
"@typescript-eslint/parser": "^8.32.1",
3244
"@vitejs/plugin-react": "^4.4.1",
45+
"autoprefixer": "^10.4.21",
3346
"eslint": "^9.27.0",
3447
"eslint-config-prettier": "^10.1.5",
3548
"eslint-plugin-prettier": "^5.4.0",
@@ -40,7 +53,9 @@
4053
"identity-obj-proxy": "^3.0.0",
4154
"jest": "^29.7.0",
4255
"jest-environment-jsdom": "^29.7.0",
56+
"postcss": "^8.5.3",
4357
"prettier": "^3.5.3",
58+
"tailwindcss": "^3.4.1",
4459
"ts-jest": "^29.3.4",
4560
"typescript": "~5.8.3",
4661
"typescript-eslint": "^8.30.1",

client/postcss.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
'@tailwindcss/postcss': {},
4+
autoprefixer: {},
5+
},
6+
}

client/src/App.tsx

Lines changed: 136 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,141 @@
1-
import './App.css';
2-
import mensaImage from './assets/meet@mensa_transparent.svg';
1+
import React from 'react';
2+
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
3+
import Layout from './components/Layout';
4+
import Login from './components/Login';
5+
import Dashboard from './components/Dashboard';
6+
import MatchRequests from './components/MatchRequests';
7+
import Invitations from './components/Invitations';
8+
import LunchMeetings from './components/LunchMeetings';
9+
import Chat from './components/Chat';
10+
import { useAuth0 } from '@auth0/auth0-react';
11+
import Profile from './components/Profile';
312

4-
function App() {
13+
interface AppProps {
14+
toggleColorMode: () => void;
15+
mode: 'light' | 'dark';
16+
}
17+
18+
// Protected Route wrapper component using Auth0
19+
const ProtectedRoute = ({ children }: { children: React.ReactNode }) => {
20+
const { isAuthenticated, isLoading } = useAuth0();
21+
22+
// Show loading state while Auth0 is initializing
23+
if (isLoading) {
24+
return (
25+
<div
26+
style={{
27+
display: 'flex',
28+
justifyContent: 'center',
29+
alignItems: 'center',
30+
height: '100vh',
31+
fontSize: '18px',
32+
}}
33+
>
34+
Loading...
35+
</div>
36+
);
37+
}
38+
39+
// Redirect to login if not authenticated
40+
return isAuthenticated ? <>{children}</> : <Navigate to="/login" />;
41+
};
42+
43+
const App = ({ toggleColorMode, mode }: AppProps) => {
544
return (
6-
<div className="app-container">
7-
<img src={mensaImage} alt="Meet@Mensa" className="mensa-image" width={600} />
8-
<h1 className="coming-soon">Coming Soon</h1>
9-
</div>
45+
<Router>
46+
<Routes>
47+
{/* Public routes */}
48+
<Route path="/login" element={<Login />} />
49+
50+
{/* Protected routes - all require authentication */}
51+
<Route
52+
path="/"
53+
element={
54+
<ProtectedRoute>
55+
<Layout toggleColorMode={toggleColorMode} mode={mode}>
56+
<Dashboard />
57+
</Layout>
58+
</ProtectedRoute>
59+
}
60+
/>
61+
<Route
62+
path="/dashboard"
63+
element={
64+
<ProtectedRoute>
65+
<Layout toggleColorMode={toggleColorMode} mode={mode}>
66+
<Dashboard />
67+
</Layout>
68+
</ProtectedRoute>
69+
}
70+
/>
71+
<Route
72+
path="/preferences"
73+
element={
74+
<ProtectedRoute>
75+
<Layout toggleColorMode={toggleColorMode} mode={mode}>
76+
<MatchRequests />
77+
</Layout>
78+
</ProtectedRoute>
79+
}
80+
/>
81+
<Route
82+
path="/invitations"
83+
element={
84+
<ProtectedRoute>
85+
<Layout toggleColorMode={toggleColorMode} mode={mode}>
86+
<Invitations />
87+
</Layout>
88+
</ProtectedRoute>
89+
}
90+
/>
91+
<Route
92+
path="/meetings"
93+
element={
94+
<ProtectedRoute>
95+
<Layout toggleColorMode={toggleColorMode} mode={mode}>
96+
<LunchMeetings />
97+
</Layout>
98+
</ProtectedRoute>
99+
}
100+
/>
101+
<Route
102+
path="/chat"
103+
element={
104+
<ProtectedRoute>
105+
<Layout toggleColorMode={toggleColorMode} mode={mode}>
106+
<Chat />
107+
</Layout>
108+
</ProtectedRoute>
109+
}
110+
/>
111+
<Route
112+
path="/profile"
113+
element={
114+
<ProtectedRoute>
115+
<Layout toggleColorMode={toggleColorMode} mode={mode}>
116+
<Profile />
117+
</Layout>
118+
</ProtectedRoute>
119+
}
120+
/>
121+
122+
{/* Account settings route */}
123+
<Route
124+
path="/account"
125+
element={
126+
<ProtectedRoute>
127+
<Layout toggleColorMode={toggleColorMode} mode={mode}>
128+
<div>Account Settings (Coming Soon)</div>
129+
</Layout>
130+
</ProtectedRoute>
131+
}
132+
/>
133+
134+
{/* Catch all route */}
135+
<Route path="*" element={<Navigate to="/" replace />} />
136+
</Routes>
137+
</Router>
10138
);
11-
}
139+
};
12140

13141
export default App;

client/src/__tests__/App.test.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import { render, screen } from '@testing-library/react';
22
import App from '../App';
33

4+
// Mock the required props
5+
const mockProps = {
6+
toggleColorMode: jest.fn(),
7+
mode: 'light' as const,
8+
};
9+
410
describe('App', () => {
511
it('renders the coming soon message', () => {
6-
render(<App />);
12+
render(<App {...mockProps} />);
713
expect(screen.getByText('Coming Soon')).toBeInTheDocument();
814
});
915

1016
it('renders the mensa image', () => {
11-
render(<App />);
17+
render(<App {...mockProps} />);
1218
const image = screen.getByAltText('Meet@Mensa');
1319
expect(image).toBeInTheDocument();
1420
});
694 KB
Loading
138 KB
Loading

client/src/auth_config.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"domain": "meetatmensa.eu.auth0.com",
3+
"clientId": "2LIYQdcRo18i5Yx2A9UUY6x2fDHw7Krz"
4+
}

client/src/components/AppBar.tsx

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import React, { useState } from 'react';
2+
import {
3+
AppBar as MuiAppBar,
4+
Box,
5+
Toolbar,
6+
IconButton,
7+
Typography,
8+
Menu,
9+
Avatar,
10+
Tooltip,
11+
MenuItem,
12+
} from '@mui/material';
13+
import MenuIcon from '@mui/icons-material/Menu';
14+
import { useNavigate } from 'react-router-dom';
15+
import { useAuth0 } from '@auth0/auth0-react';
16+
import mensaLogo from '../assets/meet@mensa_transparent.svg';
17+
import { useTheme, useMediaQuery } from '@mui/material';
18+
19+
const settings = ['Profile', 'Logout'];
20+
21+
interface AppBarProps {
22+
onHamburgerClick?: () => void;
23+
}
24+
25+
const AppBar: React.FC<AppBarProps> = ({ onHamburgerClick }) => {
26+
const [anchorElUser, setAnchorElUser] = useState<null | HTMLElement>(null);
27+
const { logout, user } = useAuth0();
28+
const navigate = useNavigate();
29+
const theme = useTheme();
30+
const smDown = useMediaQuery(theme.breakpoints.down('sm'));
31+
32+
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
33+
setAnchorElUser(event.currentTarget);
34+
};
35+
36+
const handleCloseUserMenu = () => {
37+
setAnchorElUser(null);
38+
};
39+
40+
const handleMenuClick = (setting: string) => {
41+
handleCloseUserMenu();
42+
switch (setting) {
43+
case 'Profile':
44+
navigate('/profile');
45+
break;
46+
case 'Logout':
47+
logout({ logoutParams: { returnTo: `${window.location.origin}` } });
48+
break;
49+
default:
50+
break;
51+
}
52+
};
53+
54+
// Helper to get initials from name
55+
const getInitials = (name: string) => {
56+
const [first, ...rest] = name.split(' ');
57+
const last = rest.length > 0 ? rest[rest.length - 1] : '';
58+
return `${first?.[0] || ''}${last?.[0] || ''}`.toUpperCase();
59+
};
60+
61+
return (
62+
<MuiAppBar
63+
position="fixed"
64+
sx={{
65+
backgroundColor: theme.palette.background.paper,
66+
color: theme.palette.text.primary,
67+
boxShadow: 'none',
68+
borderBottom: `1px solid ${theme.palette.divider}`,
69+
zIndex: (theme) => theme.zIndex.drawer + 1,
70+
}}
71+
>
72+
<Toolbar disableGutters sx={{ px: 0, minHeight: 64 }}>
73+
{/* Hamburger Button (nur mobil) */}
74+
{smDown && (
75+
<IconButton
76+
color="inherit"
77+
aria-label="open drawer"
78+
edge="start"
79+
onClick={onHamburgerClick}
80+
sx={{ ml: 1, mr: 1 }}
81+
>
82+
<MenuIcon
83+
sx={{ color: (theme) => (theme.palette.mode === 'dark' ? 'white' : 'black') }}
84+
/>
85+
</IconButton>
86+
)}
87+
{/* Logo ganz links */}
88+
<Box sx={{ flexGrow: 0, display: 'flex', alignItems: 'center', pl: 2 }}>
89+
<img
90+
src={mensaLogo}
91+
alt="Meet@Mensa"
92+
style={{
93+
height: smDown ? '30px' : '40px',
94+
marginTop: smDown ? '-15px' : 0,
95+
cursor: 'pointer',
96+
}}
97+
onClick={() => navigate('/')}
98+
/>
99+
</Box>
100+
101+
{/* Spacer to push user menu to the right */}
102+
<Box sx={{ flexGrow: 1 }} />
103+
104+
{/* User menu ganz rechts */}
105+
<Box sx={{ flexGrow: 0, pr: 2 }}>
106+
<Tooltip title="Open settings">
107+
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
108+
<Avatar
109+
src={user?.picture}
110+
sx={{
111+
color: (theme) => (theme.palette.mode === 'dark' ? 'white' : 'black'),
112+
bgcolor: (theme) =>
113+
theme.palette.mode === 'dark'
114+
? theme.palette.grey[800]
115+
: theme.palette.grey[200],
116+
}}
117+
>
118+
{user?.name ? getInitials(user.name) : 'U'}
119+
</Avatar>
120+
</IconButton>
121+
</Tooltip>
122+
<Menu
123+
sx={{ mt: '45px' }}
124+
id="menu-appbar"
125+
anchorEl={anchorElUser}
126+
anchorOrigin={{
127+
vertical: 'top',
128+
horizontal: 'right',
129+
}}
130+
keepMounted
131+
transformOrigin={{
132+
vertical: 'top',
133+
horizontal: 'right',
134+
}}
135+
open={Boolean(anchorElUser)}
136+
onClose={handleCloseUserMenu}
137+
>
138+
{settings.map((setting) => (
139+
<MenuItem key={setting} onClick={() => handleMenuClick(setting)}>
140+
<Typography textAlign="center">{setting}</Typography>
141+
</MenuItem>
142+
))}
143+
</Menu>
144+
</Box>
145+
</Toolbar>
146+
</MuiAppBar>
147+
);
148+
};
149+
150+
export default AppBar;

0 commit comments

Comments
 (0)