Skip to content

Commit f4e0a48

Browse files
Copilotvinod0m
andcommitted
Implement basic authentication infrastructure with login/register forms
Co-authored-by: vinod0m <221896197+vinod0m@users.noreply.github.com>
1 parent a74e4bc commit f4e0a48

File tree

10 files changed

+1326
-13
lines changed

10 files changed

+1326
-13
lines changed

frontend/App.tsx

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,61 @@
11
import React from 'react';
22
import { SettingsProvider } from './stores/settingsStore';
3+
import { AuthProvider, useAuth } from './stores/authStore';
34
import { Navigation } from './components/Navigation';
45
import { SettingsPage } from './pages/SettingsPage';
5-
import { DashboardPage } from './pages/DashboardPage';
6+
import { HomePage } from './components/HomePage';
7+
import { LoginForm } from './components/LoginForm';
8+
import { RegisterForm } from './components/RegisterForm';
9+
import { Dashboard } from './components/Dashboard';
10+
import { AdminPanel } from './components/AdminPanel';
611
import './styles.css';
712

8-
function App() {
13+
const AppContent: React.FC = () => {
14+
const { isAuthenticated, isLoading } = useAuth();
15+
916
// Simple routing based on hash
1017
const currentHash = window.location.hash.slice(1) || '/';
1118

1219
const renderPage = () => {
20+
if (isLoading) {
21+
return (
22+
<div className="min-h-screen flex items-center justify-center">
23+
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
24+
</div>
25+
);
26+
}
27+
28+
// Public routes
29+
if (currentHash === '/login') {
30+
return <LoginForm />;
31+
}
32+
33+
if (currentHash === '/register') {
34+
return <RegisterForm />;
35+
}
36+
37+
// Protected routes
38+
if (!isAuthenticated) {
39+
if (currentHash === '/') {
40+
return <HomePage />;
41+
}
42+
// Redirect to login for protected routes
43+
window.location.hash = '/login';
44+
return <LoginForm />;
45+
}
46+
47+
// Authenticated routes
1348
switch (currentHash) {
49+
case '/dashboard':
50+
return <Dashboard />;
51+
case '/admin':
52+
return <AdminPanel />;
1453
case '/settings':
1554
return <SettingsPage />;
1655
case '/':
1756
default:
18-
return <DashboardPage />;
57+
// For authenticated users, default to dashboard
58+
return <Dashboard />;
1959
}
2060
};
2161

@@ -38,6 +78,14 @@ function App() {
3878
</div>
3979
</SettingsProvider>
4080
);
81+
};
82+
83+
function App() {
84+
return (
85+
<AuthProvider>
86+
<AppContent />
87+
</AuthProvider>
88+
);
4189
}
4290

4391
export default App;

frontend/components/AdminPanel.tsx

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
import React from 'react';
2+
import { RequireAdmin } from './AuthGuards';
3+
4+
// Mock data for admin panel
5+
const mockUsers = [
6+
{
7+
id: 1,
8+
username: 'admin',
9+
email: 'admin@sdlccore.com',
10+
status: 'Active',
11+
roles: ['admin'],
12+
created: '08/15/2025'
13+
},
14+
{
15+
id: 2,
16+
username: 'developer1',
17+
email: 'dev@sdlccore.com',
18+
status: 'Active',
19+
roles: ['developer'],
20+
created: '08/16/2025'
21+
},
22+
{
23+
id: 3,
24+
username: 'analyst1',
25+
email: 'analyst@sdlccore.com',
26+
status: 'Active',
27+
roles: ['analyst'],
28+
created: '08/17/2025'
29+
}
30+
];
31+
32+
const mockRoles = [
33+
{
34+
id: 1,
35+
name: 'admin',
36+
description: 'Full system administrator access'
37+
},
38+
{
39+
id: 2,
40+
name: 'user',
41+
description: 'Standard user access'
42+
},
43+
{
44+
id: 3,
45+
name: 'moderator',
46+
description: 'Content moderation access'
47+
},
48+
{
49+
id: 4,
50+
name: 'analyst',
51+
description: 'Analytics and reporting access'
52+
},
53+
{
54+
id: 5,
55+
name: 'developer',
56+
description: 'Development and deployment access'
57+
}
58+
];
59+
60+
export const AdminPanel: React.FC = () => {
61+
return (
62+
<RequireAdmin fallback={
63+
<div className="max-w-4xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
64+
<div className="bg-red-50 border border-red-200 rounded-lg p-6 text-center">
65+
<div className="text-red-600 text-lg font-medium mb-2">Access Denied</div>
66+
<div className="text-red-600">You need administrator privileges to access this panel.</div>
67+
</div>
68+
</div>
69+
}>
70+
<div className="max-w-6xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
71+
<div className="mb-6">
72+
<h1 className="text-3xl font-bold text-gray-900">Admin Panel</h1>
73+
<p className="text-gray-600 mt-2">Manage users and roles in the system.</p>
74+
</div>
75+
76+
{/* Users Management */}
77+
<div className="bg-white shadow rounded-lg mb-8">
78+
<div className="px-6 py-4 border-b border-gray-200">
79+
<h2 className="text-lg font-semibold text-gray-900">Users Management</h2>
80+
</div>
81+
82+
<div className="overflow-x-auto">
83+
<table className="min-w-full divide-y divide-gray-200">
84+
<thead className="bg-gray-50">
85+
<tr>
86+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
87+
ID
88+
</th>
89+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
90+
Username
91+
</th>
92+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
93+
Email
94+
</th>
95+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
96+
Status
97+
</th>
98+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
99+
Roles
100+
</th>
101+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
102+
Created
103+
</th>
104+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
105+
Actions
106+
</th>
107+
</tr>
108+
</thead>
109+
<tbody className="bg-white divide-y divide-gray-200">
110+
{mockUsers.map((user) => (
111+
<tr key={user.id}>
112+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
113+
{user.id}
114+
</td>
115+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
116+
{user.username}
117+
</td>
118+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
119+
{user.email}
120+
</td>
121+
<td className="px-6 py-4 whitespace-nowrap">
122+
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
123+
user.status === 'Active'
124+
? 'bg-green-100 text-green-800'
125+
: 'bg-red-100 text-red-800'
126+
}`}>
127+
{user.status}
128+
</span>
129+
</td>
130+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
131+
<div className="flex flex-wrap gap-1">
132+
{user.roles.map((role, index) => (
133+
<span
134+
key={index}
135+
className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
136+
role === 'admin' ? 'bg-red-100 text-red-800' : 'bg-blue-100 text-blue-800'
137+
}`}
138+
>
139+
{role}
140+
</span>
141+
))}
142+
</div>
143+
</td>
144+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
145+
{user.created}
146+
</td>
147+
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
148+
<button className="text-blue-600 hover:text-blue-900 mr-2">
149+
Edit
150+
</button>
151+
<button className="text-red-600 hover:text-red-900">
152+
Delete
153+
</button>
154+
</td>
155+
</tr>
156+
))}
157+
</tbody>
158+
</table>
159+
</div>
160+
</div>
161+
162+
{/* Roles Management */}
163+
<div className="bg-white shadow rounded-lg">
164+
<div className="px-6 py-4 border-b border-gray-200">
165+
<h2 className="text-lg font-semibold text-gray-900">Roles</h2>
166+
</div>
167+
168+
<div className="px-6 py-4">
169+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
170+
{mockRoles.map((role) => (
171+
<div key={role.id} className="border border-gray-200 rounded-lg p-4">
172+
<div className="flex items-center justify-between mb-2">
173+
<h3 className={`text-lg font-semibold ${
174+
role.name === 'admin' ? 'text-red-600' : 'text-gray-900'
175+
}`}>
176+
{role.name}
177+
</h3>
178+
<span className={`inline-flex px-2 py-1 text-xs font-semibold rounded-full ${
179+
role.name === 'admin'
180+
? 'bg-red-100 text-red-800'
181+
: role.name === 'developer'
182+
? 'bg-blue-100 text-blue-800'
183+
: role.name === 'analyst'
184+
? 'bg-green-100 text-green-800'
185+
: role.name === 'moderator'
186+
? 'bg-yellow-100 text-yellow-800'
187+
: 'bg-gray-100 text-gray-800'
188+
}`}>
189+
{role.name}
190+
</span>
191+
</div>
192+
<p className="text-sm text-gray-600 mb-3">
193+
{role.description}
194+
</p>
195+
<div className="flex space-x-2">
196+
<button className="text-xs text-blue-600 hover:text-blue-900">
197+
Edit
198+
</button>
199+
<button className="text-xs text-red-600 hover:text-red-900">
200+
Delete
201+
</button>
202+
</div>
203+
</div>
204+
))}
205+
</div>
206+
</div>
207+
</div>
208+
</div>
209+
</RequireAdmin>
210+
);
211+
};

frontend/components/AuthGuards.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import React, { ReactNode } from 'react';
2+
import { useAuth } from '../stores/authStore';
3+
import { UserRole } from '../types/auth';
4+
5+
interface RequireAuthProps {
6+
children: ReactNode;
7+
fallback?: ReactNode;
8+
}
9+
10+
export const RequireAuth: React.FC<RequireAuthProps> = ({
11+
children,
12+
fallback = <div>Please log in to access this content.</div>
13+
}) => {
14+
const { isAuthenticated, isLoading } = useAuth();
15+
16+
if (isLoading) {
17+
return <div className="flex justify-center items-center h-32">
18+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
19+
</div>;
20+
}
21+
22+
if (!isAuthenticated) {
23+
return <>{fallback}</>;
24+
}
25+
26+
return <>{children}</>;
27+
};
28+
29+
interface RequireRoleProps {
30+
children: ReactNode;
31+
role: UserRole | UserRole[];
32+
fallback?: ReactNode;
33+
}
34+
35+
export const RequireRole: React.FC<RequireRoleProps> = ({
36+
children,
37+
role,
38+
fallback = <div className="text-red-600">You don't have permission to access this content.</div>
39+
}) => {
40+
const { hasRole, hasAnyRole } = useAuth();
41+
42+
const hasPermission = Array.isArray(role)
43+
? hasAnyRole(role)
44+
: hasRole(role);
45+
46+
if (!hasPermission) {
47+
return <>{fallback}</>;
48+
}
49+
50+
return <>{children}</>;
51+
};
52+
53+
export const RequireAdmin: React.FC<RequireAuthProps> = ({ children, fallback }) => (
54+
<RequireRole role="admin" fallback={fallback}>
55+
{children}
56+
</RequireRole>
57+
);
58+
59+
export const RequireModerator: React.FC<RequireAuthProps> = ({ children, fallback }) => (
60+
<RequireRole role={["admin", "moderator"]} fallback={fallback}>
61+
{children}
62+
</RequireRole>
63+
);
64+
65+
export const RequireAnalyst: React.FC<RequireAuthProps> = ({ children, fallback }) => (
66+
<RequireRole role={["admin", "analyst"]} fallback={fallback}>
67+
{children}
68+
</RequireRole>
69+
);
70+
71+
export const RequireDeveloper: React.FC<RequireAuthProps> = ({ children, fallback }) => (
72+
<RequireRole role={["admin", "developer"]} fallback={fallback}>
73+
{children}
74+
</RequireRole>
75+
);

0 commit comments

Comments
 (0)