Skip to content

Commit 71fb13f

Browse files
committed
Add frontend for profile where an API key can be generated
1 parent 4600d38 commit 71fb13f

File tree

3 files changed

+124
-5
lines changed

3 files changed

+124
-5
lines changed

frontend/src/App.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Register from './pages/Register';
66
import type {User} from './types/User';
77
import Layout from "./components/PageLayout";
88
import MyCourses from "./pages/MyCourses";
9+
import Profile from "./pages/Profile";
910

1011
function App() {
1112
const [user, setUser] = useState<User | null | undefined>(undefined);
@@ -30,7 +31,8 @@ function App() {
3031
{path: '/', element: <AllCourses/>},
3132
{path: '/my-courses', element: <MyCourses user={user}/>},
3233
{path: '/login', element: <Login setUser={setUser}/>},
33-
{path: '/register', element: <Register setUser={setUser}/>}
34+
{path: '/register', element: <Register setUser={setUser}/>},
35+
{path: '/profile', element: <Profile user={user}/>}
3436
]
3537
}]);
3638

frontend/src/components/Header.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ function Header({user, setUser}: {
3636
<div>
3737
{user ? (
3838
<div className="flex items-center gap-4">
39-
<span>
40-
{user.firstName} {user.lastName} ({user.role})
41-
</span>
39+
<Link to="/profile" className="underline hover:text-blue-600 transition">
40+
<span>
41+
{user.firstName} {user.lastName} ({user.role})
42+
</span>
43+
</Link>
4244
<button
4345
onClick={logout}
4446
className="bg-blue-600 text-white px-5 py-2 rounded-full font-semibold shadow hover:bg-blue-700 transition"
@@ -62,4 +64,3 @@ function Header({user, setUser}: {
6264
}
6365

6466
export default Header
65-

frontend/src/pages/Profile.tsx

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import React, { useState } from 'react';
2+
import { User } from '../types/User';
3+
import { Navigate } from 'react-router';
4+
5+
function Profile({ user }: { user: User | null }) {
6+
const [apiKey, setApiKey] = useState<string | null>(null);
7+
const [isGenerating, setIsGenerating] = useState(false);
8+
const [error, setError] = useState<string | null>(null);
9+
10+
if (!user) {
11+
return <Navigate to="/login" />;
12+
}
13+
14+
const generateApiKey = async () => {
15+
setIsGenerating(true);
16+
setError(null);
17+
18+
try {
19+
// TODO: Replace with actual backend endpoint when implemented
20+
// This is a placeholder that simulates API key generation
21+
setTimeout(() => {
22+
// Mock API key (in production this would come from the backend)
23+
const mockApiKey = `key_${Math.random().toString(36).substring(2, 15)}`;
24+
setApiKey(mockApiKey);
25+
setIsGenerating(false);
26+
}, 1000);
27+
28+
// Actual API call would look like:
29+
// const response = await fetch('http://localhost:8080/api/users/generate-api-key', {
30+
// method: 'POST',
31+
// credentials: 'include',
32+
// headers: {
33+
// 'Content-Type': 'application/json',
34+
// }
35+
// });
36+
// const data = await response.json();
37+
// setApiKey(data.apiKey);
38+
} catch (err) {
39+
setError('Failed to generate API key. Please try again later.');
40+
setIsGenerating(false);
41+
}
42+
};
43+
44+
return (
45+
<div className="max-w-5xl container mx-auto px-4 py-8">
46+
<h1 className="text-3xl font-bold mb-6">User Profile</h1>
47+
48+
<div className="bg-white shadow-md rounded-lg p-6 mb-6">
49+
<h2 className="text-xl font-semibold mb-4">Personal Information</h2>
50+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
51+
<div>
52+
<p className="text-sm text-gray-500">First Name</p>
53+
<p className="font-medium">{user.firstName}</p>
54+
</div>
55+
<div>
56+
<p className="text-sm text-gray-500">Last Name</p>
57+
<p className="font-medium">{user.lastName}</p>
58+
</div>
59+
<div>
60+
<p className="text-sm text-gray-500">Role</p>
61+
<p className="font-medium">{user.role}</p>
62+
</div>
63+
<div>
64+
<p className="text-sm text-gray-500">Email</p>
65+
<p className="font-medium">{user.emailAddress}</p>
66+
</div>
67+
</div>
68+
</div>
69+
70+
<div className="bg-white shadow-md rounded-lg p-6">
71+
<h2 className="text-xl font-semibold mb-4">API Access</h2>
72+
<p className="text-gray-600 mb-4">
73+
Generate an API key to access the CourseHub APIs programmatically. Keep this key secure!
74+
</p>
75+
76+
{apiKey ? (
77+
<div className="mb-4">
78+
<p className="text-sm text-gray-500 mb-1">Your API Key</p>
79+
<div className="flex items-center">
80+
<input
81+
type="text"
82+
value={apiKey}
83+
readOnly
84+
className="flex-1 border rounded-md py-2 px-3 bg-gray-50"
85+
/>
86+
<button
87+
className="ml-2 bg-gray-200 hover:bg-gray-300 px-4 py-2 rounded-md"
88+
onClick={() => {
89+
navigator.clipboard.writeText(apiKey);
90+
}}
91+
>
92+
Copy
93+
</button>
94+
</div>
95+
<p className="text-sm text-red-500 mt-2">
96+
Make sure to save this key now! You won't be able to see it again.
97+
</p>
98+
</div>
99+
) : (
100+
<>
101+
{error && <p className="text-red-500 mb-4">{error}</p>}
102+
<button
103+
onClick={generateApiKey}
104+
disabled={isGenerating}
105+
className="bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 transition disabled:bg-blue-400"
106+
>
107+
{isGenerating ? 'Generating...' : 'Generate API Key'}
108+
</button>
109+
</>
110+
)}
111+
</div>
112+
</div>
113+
);
114+
}
115+
116+
export default Profile;

0 commit comments

Comments
 (0)