Fix: Prevent Sidebar component from crashing when user data is null

Added a check to ensure the user object is not null before accessing its properties.
 This prevents the application from crashing due to a TypeError when no user data is available in localStorage.
This commit is contained in:
surajb 2024-07-18 11:44:46 +05:30
parent f900881dd7
commit 8bde35342c
12 changed files with 360 additions and 54 deletions

12
db.json
View File

@ -23,7 +23,7 @@
"email": "adminuser@example.com"
}
],
"employee": [
"employees": [
{
"id": "1",
"name": "Employee User",
@ -112,6 +112,16 @@
"email": "tejas001@gmail.com",
"address": " Baner, Pune",
"mobile": "8675493099"
},
{
"id": "e539",
"firstname": "aaaa",
"lastname": "bbbb",
"age": "25",
"gender": "male",
"email": "aaaa@gmail.com",
"address": "pune",
"mobile": "7559393995"
}
]
}

View File

@ -11,6 +11,7 @@ import CustomerMeasurements from './components/customer/CustomerMeasurements';
import { AuthProvider, AuthContext } from './contexts/AuthContext';
import EmployeeList from './components/admin/EmployeeList';
import AddEmployee from './components/admin/AddEmployee';
import EmployeeProfile from './components/admin/EmployeeProfile';
const { Content } = Layout;
@ -33,6 +34,7 @@ const App = () => {
<Route path="/measurements" element={<CustomerMeasurements />} />
<Route path="employees" element={<PrivateRoute><EmployeeList /></PrivateRoute>} />
<Route path="/add-employee" element={<PrivateRoute><AddEmployee /></PrivateRoute>} />
<Route path="/employee-profile" element={<PrivateRoute><EmployeeProfile /></PrivateRoute>} />
</Routes>
</Router>
</AuthProvider>

View File

@ -15,16 +15,16 @@ const LoginPage = () => {
try {
const response = await axiosInstance.get('http://localhost:5000/authlogin');
const user = response.data.find(user => user.email === email && user.password === password);
if (user) {
localStorage.setItem("loggedInUser", JSON.stringify(user))
if (user.role === 'admin') {
navigate('/admin');
} else if (user.role === 'employee') {
navigate('/employee');
} else {
setError('Invalid credentials');
setError('Invalid role.');
}
} else {
setError('Invalid credentials');

View File

@ -0,0 +1,48 @@
// import React, { useState } from 'react';
// import axiosInstance from '../../api/axiosConfig';
// import './ResetPasswordRequest.css';
// const ResetPasswordRequest = () => {
// const [email, setEmail] = useState('');
// const [message, setMessage] = useState('');
// const handleResetRequest = async (e) => {
// e.preventDefault();
// try {
// await axiosInstance.post('http://localhost:5000/auth/forgot-password', { email });
// setMessage('Password reset email sent. Please check your inbox.');
// } catch (error) {
// setMessage('Error sending password reset email. Please try again.');
// }
// };
// return (
// <div className="flex flex-row min-h-screen justify-center items-center">
// <div className="reset-request-page flex items-center justify-center min-h-screen w-full md:w-1/2 lg:w-1/3 p-4">
// <div className="w-full">
// <h1>Reset Password</h1>
// <p>Enter your email to receive password reset instructions.</p>
// {message && <p className="message">{message}</p>}
// <form onSubmit={handleResetRequest} className="space-y-4">
// <label htmlFor="email" className="block"></label>
// <input
// type="email"
// id="email"
// name="email"
// value={email}
// onChange={(e) => setEmail(e.target.value)}
// placeholder="Enter your email"
// required
// className="input-field"
// />
// <button type="submit" className="reset-button">
// Send Reset Instructions
// </button>
// </form>
// </div>
// </div>
// </div>
// );
// };
// export default ResetPasswordRequest;

View File

@ -39,7 +39,7 @@ const AddEmployee = () => {
<div className="w-full flex flex-col min-h-screen text-black">
<div className="min-h-screen text-gray-800 p-6">
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-semibold text-blue-900">Creating Employee Account</h1>
<h1 className="text-2xl font-semibold text-blue-900">creating employee account</h1>
<Link to="/" className="text-blue-900">Skip for now</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
@ -48,7 +48,7 @@ const AddEmployee = () => {
<div className="inline-block relative">
<div className="bg-gray-300 w-24 h-24 rounded-full mx-auto"></div>
<button className="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-#4b5563 text-white text-xs px-2 py-1 rounded-full">
Add Employee Image
add employee image
</button>
</div>
</div>
@ -56,7 +56,7 @@ const AddEmployee = () => {
<div className="md:col-span-2 space-y-4">
<form onSubmit={handleAddEmployeeSubmit}>
<div>
<label className="block text-gray-800 font-medium">Employee Firstname:</label>
<label className="block text-gray-800 font-medium">employee firstname:</label>
<input
type="text"
name="firstname"
@ -66,7 +66,7 @@ const AddEmployee = () => {
/>
</div>
<div>
<label className="block text-gray-800 font-medium">Employee Lastname:</label>
<label className="block text-gray-800 font-medium">employee lastname:</label>
<input
type="text"
name="lastname"
@ -76,7 +76,7 @@ const AddEmployee = () => {
/>
</div>
<div>
<label className="block text-gray-800 font-medium">Age:</label>
<label className="block text-gray-800 font-medium">age:</label>
<input
type="number"
name="age"
@ -86,7 +86,7 @@ const AddEmployee = () => {
/>
</div>
<div>
<label className="block text-gray-800 font-medium">Gender:</label>
<label className="block text-gray-800 font-medium">gender:</label>
<select
name="gender"
value={employeeData.gender}
@ -94,13 +94,13 @@ const AddEmployee = () => {
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
>
<option value="" disabled>Select Gender</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
<option value="male">male</option>
<option value="female">female</option>
<option value="other">other</option>
</select>
</div>
<div>
<label className="block text-gray-800 font-medium">Email:</label>
<label className="block text-gray-800 font-medium">email:</label>
<input
type="email"
name="email"
@ -110,7 +110,7 @@ const AddEmployee = () => {
/>
</div>
<div>
<label className="block text-gray-800 font-medium">Address:</label>
<label className="block text-gray-800 font-medium">address:</label>
<input
type="text"
name="address"
@ -120,7 +120,7 @@ const AddEmployee = () => {
/>
</div>
<div>
<label className="block text-gray-800 font-medium">Mobile No:</label>
<label className="block text-gray-800 font-medium">mobile no:</label>
<input
type="text"
name="mobile"
@ -132,7 +132,7 @@ const AddEmployee = () => {
<br />
<div className="text-center">
<button type="submit" className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md font-semibold">
Create Account
create account
</button>
</div>
</form>

View File

@ -25,12 +25,12 @@ const EmployeeList = ({ className = '' }) => {
};
const handleAddEmployee = () => {
navigate('/add-employee');
};
navigate('/add-employee');
};
// const onEmployeeCardsClick = useCallback(() => {
// navigate('/employee-profile');
// }, [navigate]);
const onEmployeeCardsClick = useCallback(() => {
navigate('/employee-profile');
}, [navigate]);
return (
<div className="flex flex-row min-h-screen">
@ -38,12 +38,12 @@ const EmployeeList = ({ className = '' }) => {
<div className="flex-grow p-6 bg-gray-100">
<div className="bg-white p-6 rounded-lg shadow">
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-semibold text-gray-800">Employees List</h1>
<h1 className="text-2xl font-semibold text-gray-800">employees list</h1>
<button
className="bg-#0e355b text-d1d5db px-4 py-2 rounded-md text-sm"
onClick={handleAddEmployee}
>
Add New Employee
add new employee
</button>
</div>
<div className="flex items-center mb-4">
@ -61,16 +61,16 @@ const EmployeeList = ({ className = '' }) => {
d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
/>
</svg>
<span className="text-sm text-gray-600">Filter</span>
<span className="text-sm text-gray-600">filter</span>
</div>
<p className="text-sm text-gray-600 mb-4">Total Employees ({employees.length})</p>
<p className="text-sm text-gray-600 mb-4">total employees ({employees.length})</p>
<table className="w-full bg-white">
<thead>
<tr className="text-left text-xs uppercase text-gray-600 border-b">
<th className="py-2">#</th>
<th className="py-2">Employee Name</th>
<th className="py-2">Email Address</th>
<th className="py-2">Details</th>
<th className="py-2">employee name</th>
<th className="py-2">email address</th>
<th className="py-2">details</th>
</tr>
</thead>
<tbody>
@ -82,9 +82,9 @@ const EmployeeList = ({ className = '' }) => {
<td className="py-2 text-gray-800">
<button
className="text-#4b5563 underline"
// onClick={onEmployeeCardsClick}
onClick={onEmployeeCardsClick}
>
View Details
view details
</button>
</td>
</tr>

View File

@ -0,0 +1,157 @@
import React, { useCallback } from "react";
import { useNavigate, useLocation } from "react-router-dom";
import Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig';
// import ActionButtons from "../components/ActionButtons";
// import Profile from "../components/Profile";
const EmployeeProfile = () => {
const navigate = useNavigate();
const location = useLocation();
const { employee } = location.state || {};
const onButtonLabelsContainerClick = useCallback(() => {
navigate("/adminemployee-account-delete");
}, [navigate]);
const onHomeIconClick = useCallback(() => {
navigate("/adminhome");
}, [navigate]);
const onHeadingContainerClick = useCallback(() => {
navigate("/adminemployee-list");
}, [navigate]);
if (!employee) {
return <div>Employee data not found</div>;
}
return (
<div className="w-full relative bg-brooks-color-system-secondary-brand overflow-hidden flex flex-row items-start justify-start leading-[normal] tracking-[normal]">
<div className="overflow-hidden flex flex-col items-start justify-start pt-4 px-5 pb-[42px] gap-[136px] z-[1] mq450:pb-5 mq450:box-border mq825:pt-5 mq825:pb-[27px] mq825:box-border">
<div className="flex flex-row items-start justify-start pt-0 px-0 pb-2.5">
<img
className="h-10 w-10 relative"
loading="lazy"
alt=""
src="/brooks-logo3.svg"
/>
</div>
<div className="flex flex-row items-start justify-start py-0 pr-2 pl-[7px]">
<div className="flex flex-col items-start justify-start gap-[60px]">
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt=""
src="/home-icon3.svg"
onClick={onHomeIconClick}
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0"
loading="lazy"
alt=""
src="/customer-list-icon3.svg"
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0"
loading="lazy"
alt=""
src="/orders-iocn3.svg"
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0"
loading="lazy"
alt=""
src="/category-icon3.svg"
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0"
loading="lazy"
alt=""
src="/catalog-icon3.svg"
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0"
loading="lazy"
alt=""
src="/employee-icon3.svg"
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0"
loading="lazy"
alt=""
src="/measurments-iocn3.svg"
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0"
loading="lazy"
alt=""
src="/account-icon3.svg"
/>
</div>
</div>
<div className="w-10 flex flex-row items-start justify-start py-0 pr-2 pl-[7px] box-border">
<div className="flex flex-col items-start justify-start">
<img
className="w-6 h-6 relative overflow-hidden shrink-0"
loading="lazy"
alt=""
src="/materialsymbolslogout3.svg"
/>
</div>
</div>
</div>
<main className="flex-1 flex flex-col items-start justify-start pt-14 px-0 pb-0 box-border max-w-[calc(100%_-_80px)] mq450:pt-[23px] mq450:box-border mq825:pt-9 mq825:box-border">
<section className="self-stretch flex flex-col items-start justify-start gap-[35.3px] max-w-full text-center text-5xl text-brooks-color-system-text-colors-secondary-text font-button mq675:gap-[18px]">
<header className="self-stretch flex flex-col items-start justify-start gap-[26px] max-w-full text-center text-29xl text-brooks-color-system-text-colors-primary-text font-button">
<div className="flex flex-row items-start justify-start py-0 pr-5 pl-0 box-border">
<div className="flex flex-col items-start justify-start gap-7">
<div
className="relative tracking-[0.03em] leading-[normal] font-semibold flex items-center w-[250px] h-10 shrink-0 cursor-pointer"
onClick={onHeadingContainerClick}
>
<span className="text-xs tracking-[0.03em] leading-[normal] uppercase font-semibold text-brooks-color-system-text-colors-primary-text inline-block w-[170px] h-6 shrink-0 text-left z-[0]">
Employee List
</span>
<img
className="w-[19px] h-[19px] relative z-[1] ml-[9px]"
loading="lazy"
alt=""
src="/icon5.svg"
/>
</div>
<div className="relative leading-[normal] font-semibold inline-block w-[270px] h-[30px] shrink-0">
Employee Profile
</div>
</div>
</div>
</header>
<Profile
employeeName={`${employee.firstname} ${employee.lastname}`}
employeeID={employee.id}
/>
</section>
<section className="self-stretch flex flex-col items-start justify-start gap-[150px] max-w-full mq675:gap-[85px]">
<ActionButtons employeeId={employee.id} />
<footer className="self-stretch flex flex-col items-start justify-start pt-0 px-5 pb-[42px] gap-[66px] max-w-full text-left text-8xl text-brooks-color-system-text-colors-tertiary-text font-button mq450:pb-5 mq450:box-border">
<div className="flex flex-col items-start justify-start gap-[30px] mq450:gap-[18px]">
<div className="self-stretch relative leading-[normal] inline-block w-[300px] h-0 shrink-0 [transform:_rotate(90deg)] text-left [transform-origin:0_0] mq450:h-[15px]">
Employee Actions
</div>
<div
className="relative tracking-[0.03em] leading-[normal] font-semibold text-29xl inline-block w-[150px] h-[60px] shrink-0 text-center z-[0] mq675:w-[170px]"
onClick={onButtonLabelsContainerClick}
>
<span className="inline-block w-[170px] h-10 shrink-0 text-29xl font-semibold text-center text-brooks-color-system-text-colors-secondary-text leading-[normal] uppercase tracking-[0.03em]">
Delete Account
</span>
</div>
</div>
</footer>
</section>
</main>
</div>
);
};
export default EmployeeProfile;

View File

@ -41,8 +41,8 @@ const AddCustomer = () => {
<div className="w-full flex flex-col min-h-screen text-white">
<div className="min-h-screen text-gray-300 p-6">
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-semibold text-#0e355b">add customer</h1>
<Link to="/" className="text-#0e355b">skip for now</Link>
<h1 className="text-2xl font-semibold text-#0e355b" > add customer</h1>
<Link to="/CategoryList" className="text-#0e355b">skip for now</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="space-y-6">
@ -55,7 +55,7 @@ const AddCustomer = () => {
</div>
</div>
<div>
<Link to="/">
<Link to="/add-customer">
<h2 className="text-lg font-semibold text-">customer information</h2>
</Link>
<Link to="/measurements">

View File

@ -68,9 +68,11 @@ const CustomerMeasurements = () => {
<Input className="block text-sm font-medium text-#4b5563" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit" className="w-full py-4 bg-#0e355b text-#d1d5db rounded-md font-semibold mb-4 hover:bg-#154676">
Save Measurements
</Button>
<div className="text-center">
<button type="submit" className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md font-semibold text-sm">
save measurements
</button>
</div>
</Form.Item>
</Form>
</div>

View File

@ -1,12 +1,19 @@
import React, { useContext } from 'react';
import React, { useState, useEffect } from 'react';
import AdminSidebar from './AdminSidebar';
import EmployeeSidebar from './EmployeeSidebar';
import { AuthContext } from '../../contexts/AuthContext';
const Sidebar = () => {
const { user } = useContext(AuthContext);
const [user, setUserData] = useState(null);
if (!user) return null;
useEffect(() => {
// Retrieve user data from localStorage
const storedUserData = localStorage.getItem('loggedInUser');
if (storedUserData) {
setUserData(JSON.parse(storedUserData));
}
}, []);
if (!user) return null; // Ensure user is not null before accessing its properties
return user.role === 'admin' ? <AdminSidebar /> : <EmployeeSidebar />;
};

View File

@ -1,17 +1,36 @@
const authService = {
login: async (username, password) => {
// Simulate login process
const userData = { username, role: username === 'admin' ? 'admin' : 'employee' };
login: async (username, password) => {
try {
// Simulate login process with API call (replace with real API endpoint)
const response = await axiosInstance.post('/auth/login', { username, password });
const userData = response.data;
// Store user data and token in localStorage
localStorage.setItem('user', JSON.stringify(userData));
localStorage.setItem('token', userData.token);
return userData;
},
logout: () => {
localStorage.removeItem('user');
},
getCurrentUser: () => {
return JSON.parse(localStorage.getItem('user'));
} catch (error) {
throw new Error('Login failed. Please check your credentials.');
}
};
export default authService;
},
logout: () => {
localStorage.removeItem('user');
localStorage.removeItem('token');
},
getCurrentUser: () => {
return JSON.parse(localStorage.getItem('user'));
},
getToken: () => {
return localStorage.getItem('token');
},
isAuthenticated: () => {
return !!localStorage.getItem('token');
},
hasRole: (role) => {
const user = JSON.parse(localStorage.getItem('user'));
return user && user.role === role;
}
};
export default authService;

61
src/services/server.js Normal file
View File

@ -0,0 +1,61 @@
// server.js
const express = require('express');
const bodyParser = require('body-parser');
const nodemailer = require('nodemailer');
const crypto = require('crypto');
const app = express();
const port = 5000;
app.use(bodyParser.json());
let users = [
// Example users
{ email: 'admin@example.com', password: 'admin123', role: 'admin' },
{ email: 'employee@example.com', password: 'employee123', role: 'employee' },
];
// Endpoint to handle password reset requests
app.post('/auth/forgot-password', (req, res) => {
const { email } = req.body;
const user = users.find(user => user.email === email);
if (!user) {
return res.status(404).json({ message: 'User not found' });
}
const token = crypto.randomBytes(20).toString('hex');
// Store token in a way that it can be verified later
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
// Send email with reset link
const transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'your-email@gmail.com',
pass: 'your-email-password',
},
});
const mailOptions = {
to: user.email,
from: 'passwordreset@example.com',
subject: 'Password Reset',
text: `You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n
Please click on the following link, or paste this into your browser to complete the process:\n\n
http://localhost:3000/reset-password/${token}\n\n
If you did not request this, please ignore this email and your password will remain unchanged.\n`,
};
transporter.sendMail(mailOptions, (err) => {
if (err) {
return res.status(500).json({ message: 'Error sending email' });
}
res.status(200).json({ message: 'Password reset email sent' });
});
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});