Skip to content
Draft
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
6 changes: 4 additions & 2 deletions team-nishita-dashboard/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { useState, useEffect } from 'react';
import { Route, Routes, useNavigate } from 'react-router-dom';
import { jwtDecode } from 'jwt-decode'
import Navbar from './components/Navbar';
import Home from './components/Home';
import UserHome from './components/UserHome';
import AdminHome from './components/AdminHome';
import Signup from './components/Signup';
import Login from './components/Login';
interface User {
Expand Down Expand Up @@ -59,7 +60,8 @@ function App() {
<Navbar user={user} onLogout={handleLogout} />
<div>
<Routes>
<Route path='/home' element={<Home />} />
<Route path='/userHome' element={<UserHome />} />
<Route path='/adminHome' element={<AdminHome />} />
<Route path='/login' element={<Login onLogin={handleLogin} />} />
<Route path='/signup' element={<Signup onLogin={handleLogin} />} />
</Routes>
Expand Down
13 changes: 10 additions & 3 deletions team-nishita-dashboard/client/src/api/api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,17 @@ api.interceptors.request.use((config) => {
return config;
})

export interface Credentials {
export interface SignUpCredentials {
username: string,
password: string,
role: string | undefined,
};


export interface LoginCredentials {
username: string,
password: string
};
export const login = (credentials: Credentials) => api.post('/login', credentials);
export const signup = (credentials: Credentials) => api.post('/register', credentials);
export const login = (credentials: LoginCredentials) => api.post('/login', credentials);
export const signup = (credentials: SignUpCredentials) => api.post('/register', credentials);
export default api;
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Globe, Lock, ShieldCheck } from 'lucide-react';
import { Link } from 'react-router-dom';

const Home = () => {
const AdminHome = () => {

const logged = Boolean(localStorage.getItem('token'));
return (
Expand Down Expand Up @@ -33,4 +32,4 @@ const Home = () => {
);
};

export default Home;
export default AdminHome;
12 changes: 9 additions & 3 deletions team-nishita-dashboard/client/src/components/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useState, useEffect } from "react";
import { useNavigate } from 'react-router-dom';
import { login } from '../api/api';
import type { Credentials as FormData } from '../api/api'
import type { LoginCredentials as FormData } from '../api/api'

type UserRole = 'user' | 'admin';

interface LoginProps {
onLogin: (user: { username: string }) => void;
Expand All @@ -14,7 +15,7 @@ const Login = ({ onLogin }: LoginProps) => {
const [status, setStatus] = useState<string>("");
const navigate = useNavigate();

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const handleChange = (event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
setFormData({ ...formData, [event.target.name]: event.target.value });
};

Expand All @@ -24,7 +25,12 @@ const Login = ({ onLogin }: LoginProps) => {
const response = await login(formData);
localStorage.setItem('token', response.data.access_token);
onLogin({ username: formData.username });
navigate('/home');
const role: UserRole = response.data.role;
if (role === 'user') {
navigate('/userHome')
} else {
navigate('/adminHome')
}
} catch (error) {
console.error(error);
setStatus("Login failed. Please check your credentials.");
Expand Down
30 changes: 25 additions & 5 deletions team-nishita-dashboard/client/src/components/Signup.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { useState, useEffect } from "react";
import { useNavigate } from 'react-router-dom';
import { signup } from '../api/api';
import type { Credentials as FormData } from '../api/api'
import type { SignUpCredentials as FormData } from '../api/api'


interface SignupProps {
onLogin: (user: { username: string }) => void;
onLogin: (user: { username: string, role: string }) => void;
}

const Signup = ({ onLogin }: SignupProps) => {
const [formData, setFormData] = useState<FormData>({ username: "", password: "" });
const [formData, setFormData] = useState<FormData>({ username: "", password: "", role: "" });
const [showPassword, setShowPassword] = useState<boolean>(false);
const [status, setStatus] = useState<string>("");
const navigate = useNavigate();

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const handleChange = (event: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
setFormData({ ...formData, [event.target.name]: event.target.value });
};

Expand All @@ -23,7 +23,10 @@ const Signup = ({ onLogin }: SignupProps) => {
try {
const response = await signup(formData);
localStorage.setItem('token', response.data.access_token);
onLogin({ username: formData.username });
if (formData.role === undefined) {
throw new Error('Provide User Role');
}
onLogin({ username: formData.username, role: formData.role });
navigate('/home');
} catch (error) {
console.error(error);
Expand Down Expand Up @@ -84,6 +87,23 @@ const Signup = ({ onLogin }: SignupProps) => {
{showPassword ? 'Hide' : 'Show'}
</button>
</div>
<div>
<label className="block mb-2 text-sm font-medium text-emerald-700" htmlFor="username">
Role
</label>
<select
className="py-2 px-3 w-full rounded-lg border border-emerald-300 focus:ring-2 focus:ring-emerald-500 focus:outline-none bg-emerald-100/50"
id="role"
// type=""
name="role"
value={formData.role}
onChange={handleChange}
required
> <option value="">Select a role</option>
<option value="user">User</option>
<option value="admin">Admin</option>
</select>
</div>
</div>
<div>
<button
Expand Down
35 changes: 35 additions & 0 deletions team-nishita-dashboard/client/src/components/UserHome.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Link } from 'react-router-dom';

const UserHome = () => {

const logged = Boolean(localStorage.getItem('token'));
return (
<div className="flex flex-col w-full min-h-screen bg-gradient-to-br from-green-50 to-emerald-100">


<main className="grid flex-grow gap-12 items-center px-6 mt-16 md:grid-cols-2">

<div>
<h2 className="mb-6 text-5xl font-bold text-emerald-900">
TODO
</h2>
<div className="flex space-x-4">
<Link to='/login'>
{/* <button className="py-3 px-6 text-white bg-emerald-600 rounded-full transition hover:bg-emerald-700"> */}
{/* {logged ? 'Dashboard' : 'Login'} */}
{/* </button> */}
</Link>
</div>
</div>
</main>

<footer className="py-8 px-6 mt-16 text-center">
<p className="text-emerald-800">
© 2025.
</p>
</footer>
</div>
);
};

export default UserHome;
3 changes: 2 additions & 1 deletion team-nishita-dashboard/server/middleware/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const JWT_EXPIRY = '1h';
export interface JWTPayload {
id: string;
username: string;
role: string;
sub: string;
}
interface AuthenticatedRequest extends Request {
Expand All @@ -32,6 +33,6 @@ export function jwtMiddleware(req: AuthenticatedRequest, res: Response, next: Ne
return;
}
}
export function tokenGeneration(payload: { id: string, username: string }) {
export function tokenGeneration(payload: { id: string, username: string, role: string }) {
return jwt.sign({ id: payload.id, username: payload.username, sub: payload.username }, JWT_SECRET, { expiresIn: JWT_EXPIRY })
}
4 changes: 3 additions & 1 deletion team-nishita-dashboard/server/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import mongoose, { Document } from 'mongoose';
interface IUser extends Document {
username: string;
password: string;
role: string;
};

const userSchema = new mongoose.Schema<IUser>({
username: { type: String, required: true, unique: true },
password: { type: String, required: true }
password: { type: String, required: true },
role: { type: String, required: true }
})

export const User = mongoose.model('User', userSchema);
Loading