reset password request authentication and route to the login page

This commit is contained in:
surajb 2024-07-23 13:42:51 +05:30
parent 89d5b17dc1
commit fc73ddefca
12 changed files with 23214 additions and 1275 deletions

15826
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
"proxy": "http://localhost:5000", "proxy": "http://localhost:5000",
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.3.7", "@ant-design/icons": "^5.3.7",
"@craco/craco": "^7.1.0",
"@fortawesome/fontawesome-svg-core": "^6.6.0", "@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.6.0", "@fortawesome/free-solid-svg-icons": "^6.6.0",
"@fortawesome/react-fontawesome": "^0.2.2", "@fortawesome/react-fontawesome": "^0.2.2",
@ -30,14 +31,14 @@
"@types/react": "^18.2.66", "@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22", "@types/react-dom": "^18.2.22",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.19", "autoprefixer": "^9.8.8",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1", "eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-react-refresh": "^0.4.6",
"json-server": "^1.0.0-beta.1", "json-server": "^1.0.0-beta.1",
"postcss": "^8.4.39", "postcss": "^7.0.39",
"tailwindcss": "^3.4.6", "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
"vite": "^5.2.0" "vite": "^5.2.0"
} }
} }

View File

@ -2,6 +2,7 @@ import React, { useContext } from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'; import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
import { Layout } from 'antd'; import { Layout } from 'antd';
import LoginPage from './components/Auth/LoginPage'; import LoginPage from './components/Auth/LoginPage';
import ResetPasswordRequest from './components/Auth/ResetPasswordRequest';
import AdminDashboard from './components/admin/AdminDashboard'; import AdminDashboard from './components/admin/AdminDashboard';
import EmployeeDashboard from './components/employee/EmployeeDashboard'; import EmployeeDashboard from './components/employee/EmployeeDashboard';
import CustomerList from './components/customer/CustomerList'; import CustomerList from './components/customer/CustomerList';
@ -25,6 +26,7 @@ const App = () => {
<Router> <Router>
<Routes> <Routes>
<Route exact path="/" element={<LoginPage />} /> <Route exact path="/" element={<LoginPage />} />
<Route path="/reset-password" element={<ResetPasswordRequest />} />
<Route path="/employee/home" element={<EmployeeDashboard />} /> {/* Add HomePage route */} <Route path="/employee/home" element={<EmployeeDashboard />} /> {/* Add HomePage route */}
<Route path="/admin" element={<PrivateRoute><AdminDashboard /></PrivateRoute>} /> <Route path="/admin" element={<PrivateRoute><AdminDashboard /></PrivateRoute>} />
<Route path="/employee" element={<PrivateRoute><EmployeeDashboard /></PrivateRoute>} /> <Route path="/employee" element={<PrivateRoute><EmployeeDashboard /></PrivateRoute>} />

View File

@ -8,6 +8,7 @@ body, html {
/* color: #fff; */ /* color: #fff; */
} }
.login-page { .login-page {
display: flex; display: flex;
align-items: center; align-items: center;
@ -39,6 +40,12 @@ body, html {
align-items: center; align-items: center;
} }
.logo {
display: block;
margin: 0 auto 20px;
width: 100px;
}
h1 { h1 {
margin-top: 0; margin-top: 0;
color: #0e355b; color: #0e355b;

View File

@ -2,6 +2,7 @@ import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import axiosInstance from '../../api/axiosConfig'; import axiosInstance from '../../api/axiosConfig';
import './LoginPage.css'; import './LoginPage.css';
import BrookslogoIcon from '../../assets/thob-data/BrookslogoIcon.svg';
const LoginPage = () => { const LoginPage = () => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
@ -12,7 +13,7 @@ const LoginPage = () => {
const handleLogin = async (e) => { const handleLogin = async (e) => {
e.preventDefault(); e.preventDefault();
setError(''); setError('');
try { try {
const response = await axiosInstance.get('http://localhost:5000/authlogin'); const response = await axiosInstance.get('http://localhost:5000/authlogin');
const user = response.data.find(user => user.email === email && user.password === password); const user = response.data.find(user => user.email === email && user.password === password);
@ -39,11 +40,12 @@ const LoginPage = () => {
} }
} }
}; };
return ( return (
<div className="flex flex-row min-h-screen justify-center items-center"> <div className="flex flex-row min-h-screen justify-center items-center">
<div className="login-page flex items-center justify-center min-h-screen w-full md:w-1/2 lg:w-1/3 p-4"> <div className="login-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"> <div className="w-full">
<img src={BrookslogoIcon} alt="Logo" className="logo" />
<h1>welcome back</h1> <h1>welcome back</h1>
<p>be among the first to experience 3D magic! register for private alpha.</p> <p>be among the first to experience 3D magic! register for private alpha.</p>
{error && <p className="error-message">{error}</p>} {error && <p className="error-message">{error}</p>}
@ -71,7 +73,7 @@ const LoginPage = () => {
className="input-field" className="input-field"
/> />
<div className="forgot-password-container"> <div className="forgot-password-container">
<a href="#">Forgot password?</a> <a href="/reset-password">Forgot password?</a>
</div> </div>
<button type="submit" className="login-button"> <button type="submit" className="login-button">
login login

View File

@ -1,6 +1,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import axiosInstance from '../../api/axiosConfig'; import axiosInstance from '../../api/axiosConfig';
import './ResetPasswordRequest.css';
const ResetPasswordRequest = () => { const ResetPasswordRequest = () => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');

View File

@ -4,9 +4,9 @@ import Sidebar from '../sidebar/Sidebar';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons'; import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import axiosInstance from '../../api/axiosConfig'; import axiosInstance from '../../api/axiosConfig';
import { styles } from '../../styles/classNames';
import '../../styles/AddEmployee.css'; import '../../styles/AddEmployee.css';
const AddEmployee = () => { const AddEmployee = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [employeeData, setEmployeeData] = useState({ const [employeeData, setEmployeeData] = useState({
@ -15,11 +15,13 @@ const AddEmployee = () => {
age: '', age: '',
gender: '', gender: '',
email: '', email: '',
address_line_1: '', address: {
address_line_2: '', address_line_1: '',
nearby_landmark: '', address_line_2: '',
pincode: '', nearby_landmark: '',
city_id: '', pincode: '',
city_id: '',
},
mobile: '', mobile: '',
image: null, image: null,
}); });
@ -83,31 +85,31 @@ const AddEmployee = () => {
}; };
return ( return (
<div className="flex flex-row"> <div className={styles.container}>
<Sidebar /> <Sidebar />
<div className="w-full flex flex-col min-h-screen text-black"> <div className={styles.content}>
<div className="min-h-screen text-secondary p-6 bg-light"> <div className={styles.header}>
<div className="flex items-center "> <div className="flex items-center ">
<Link to="/employees" className="text-primary text-xl "> <Link to="/employees" className={styles.backButton}>
<FontAwesomeIcon icon={faChevronLeft} /> <FontAwesomeIcon icon={faChevronLeft} />
</Link> </Link>
<h1 className="text-2xl font-semibold text-primary"> <h1 className={styles.title}>
create employee account create employee account
</h1> </h1>
</div> </div>
<div className="flex justify-center"> <div className="flex justify-center">
<div className="space-y-6 w-full max-w-4xl"> <div className={`${styles.centerText} space-y-6 w-full max-w-4xl`}>
<div className="text-center"> <div className={styles.centerText}>
<div className="inline-block relative"> <div className="inline-block relative">
<div className="bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden"> <div className={styles.imageWrapper}>
{imagePreview ? ( {imagePreview ? (
<img <img
src={imagePreview} src={imagePreview}
alt="Employee" alt="Employee"
className="w-full h-full object-cover" className={styles.imagePreview}
/> />
) : ( ) : (
<div className="w-full h-full flex items-center justify-center text-gray-500"> <div className={styles.noImageText}>
No image No image
</div> </div>
)} )}
@ -122,14 +124,14 @@ const AddEmployee = () => {
<button <button
type="button" type="button"
onClick={() => document.getElementById('imageUpload').click()} onClick={() => document.getElementById('imageUpload').click()}
className="image-upload-btn" className={styles.imageUploadButton}
> >
Upload Upload
</button> </button>
</div> </div>
</div> </div>
<form onSubmit={handleAddEmployeeSubmit} className="space-y-4"> <form onSubmit={handleAddEmployeeSubmit} className={styles.form}>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <div className={styles.formGroup}>
<div> <div>
<label className="block text-secondary font-medium"> <label className="block text-secondary font-medium">
employee firstname: employee firstname:
@ -139,7 +141,7 @@ const AddEmployee = () => {
name="firstname" name="firstname"
value={employeeData.firstname} value={employeeData.firstname}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
/> />
</div> </div>
<div> <div>
@ -151,7 +153,7 @@ const AddEmployee = () => {
name="lastname" name="lastname"
value={employeeData.lastname} value={employeeData.lastname}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
/> />
</div> </div>
<div> <div>
@ -163,7 +165,7 @@ const AddEmployee = () => {
name="age" name="age"
value={employeeData.age} value={employeeData.age}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
/> />
</div> </div>
<div> <div>
@ -174,7 +176,7 @@ const AddEmployee = () => {
name="gender" name="gender"
value={employeeData.gender} value={employeeData.gender}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
> >
<option value="" disabled> <option value="" disabled>
select gender select gender
@ -193,7 +195,7 @@ const AddEmployee = () => {
name="mobile" name="mobile"
value={employeeData.mobile} value={employeeData.mobile}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
/> />
</div> </div>
<div className="md:col-span-2 lg:col-span-3"> <div className="md:col-span-2 lg:col-span-3">
@ -205,7 +207,7 @@ const AddEmployee = () => {
name="address_line_1" name="address_line_1"
value={employeeData.address_line_1} value={employeeData.address_line_1}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
/> />
</div> </div>
<div className="md:col-span-2 lg:col-span-3"> <div className="md:col-span-2 lg:col-span-3">
@ -217,7 +219,7 @@ const AddEmployee = () => {
name="address_line_2" name="address_line_2"
value={employeeData.address_line_2} value={employeeData.address_line_2}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
/> />
</div> </div>
<div className="md:col-span-2 lg:col-span-3"> <div className="md:col-span-2 lg:col-span-3">
@ -229,7 +231,7 @@ const AddEmployee = () => {
name="nearby_landmark" name="nearby_landmark"
value={employeeData.nearby_landmark} value={employeeData.nearby_landmark}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
/> />
</div> </div>
<div className="md:col-span-2 lg:col-span-3"> <div className="md:col-span-2 lg:col-span-3">
@ -241,7 +243,7 @@ const AddEmployee = () => {
name="pincode" name="pincode"
value={employeeData.pincode} value={employeeData.pincode}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
/> />
</div> </div>
<div className="md:col-span-2 lg:col-span-3"> <div className="md:col-span-2 lg:col-span-3">
@ -252,7 +254,7 @@ const AddEmployee = () => {
name="city_id" name="city_id"
value={employeeData.city_id} value={employeeData.city_id}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
> >
<option value="" disabled> <option value="" disabled>
select city select city
@ -274,14 +276,14 @@ const AddEmployee = () => {
name="email" name="email"
value={employeeData.email} value={employeeData.email}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 focus-border" className={styles.input}
/> />
</div> </div>
</div> </div>
<div className="text-center mt-6"> <div className={`${styles.centerText} mt-6`}>
<button <button
type="submit" type="submit"
className="btn-primary" className={styles.button}
> >
create account create account
</button> </button>

View File

@ -5,6 +5,32 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import Sidebar from '../sidebar/Sidebar'; import Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig'; import axiosInstance from '../../api/axiosConfig';
const CLASSES = {
container: 'flex flex-row',
mainContent: 'w-full flex flex-col min-h-screen text-black',
innerContent: 'min-h-screen text-gray-800 p-6',
header: 'flex justify-between items-center',
headerTitle: 'flex items-center',
backButton: 'text-[#0e355b] text-xl',
title: 'text-2xl font-semibold text-[#0e355b]',
skipLink: 'text-blue-900',
formGrid: 'grid grid-cols-1 md:grid-cols-3 gap-6',
imageSection: 'space-y-6',
imageWrapper: 'text-center',
imageContainer: 'inline-block relative',
imagePreview: 'bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden',
noImage: 'w-full h-full flex items-center justify-center text-gray-500',
hiddenInput: 'hidden',
uploadButton:
'absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-gray-700 text-white text-xs px-2 py-1 rounded-full',
linkTitle: 'text-lg font-semibold',
formSection: 'md:col-span-2 space-y-4',
formGroup: 'block text-gray-800 font-medium',
formInput: 'w-full p-2 border-b border-gray-400 focus:border-gray-700',
formSelect: 'w-full p-2 border-b border-gray-400 focus:border-gray-700',
submitButton: 'bg-[#0e355b] text-white py-2 px-4 rounded-md',
};
const AddCustomer = () => { const AddCustomer = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [customerData, setCustomerData] = useState({ const [customerData, setCustomerData] = useState({
@ -13,14 +39,17 @@ const AddCustomer = () => {
age: '', age: '',
gender: '', gender: '',
email: '', email: '',
address_line_1: '',
address_line_2: '',
nearby_landmark: '',
pincode: '',
city_id: '',
mobile: '', mobile: '',
image: null, image: null,
address: {
address_line_1: '',
address_line_2: '',
nearby_landmark: '',
pincode: '',
city_id: '',
},
}); });
const [cities, setCities] = useState([]); const [cities, setCities] = useState([]);
const [imagePreview, setImagePreview] = useState(null); const [imagePreview, setImagePreview] = useState(null);
@ -43,10 +72,21 @@ const AddCustomer = () => {
const handleInputChange = (e) => { const handleInputChange = (e) => {
const { name, value } = e.target; const { name, value } = e.target;
setCustomerData({ setCustomerData((prevData) => ({
...customerData, ...prevData,
[name]: value, [name]: value,
}); }));
};
const handleAddressChange = (e) => {
const { name, value } = e.target;
setCustomerData((prevData) => ({
...prevData,
address: {
...prevData.address,
[name]: value,
},
}));
}; };
const handleImageChange = (e) => { const handleImageChange = (e) => {
@ -64,16 +104,26 @@ const AddCustomer = () => {
e.preventDefault(); e.preventDefault();
const formData = new FormData(); const formData = new FormData();
Object.keys(customerData).forEach((key) => { Object.keys(customerData).forEach((key) => {
formData.append(key, customerData[key]); if (key === 'address') {
Object.keys(customerData.address).forEach((addrKey) => {
formData.append(`address[${addrKey}]`, customerData.address[addrKey]);
});
} else {
formData.append(key, customerData[key]);
}
}); });
try { try {
const response = await axiosInstance.post('http://localhost:5000/customers', formData, { const response = await axiosInstance.post(
headers: { 'http://localhost:5000/customers',
'Content-Type': 'multipart/form-data', formData,
}, {
}); headers: {
console.log('Raw response:', response); 'Content-Type': 'multipart/form-data',
},
}
);
console.log('Raw response:', response);
console.log('Customer added successfully:', response.data); console.log('Customer added successfully:', response.data);
navigate('/customers'); navigate('/customers');
} catch (error) { } catch (error) {
@ -82,26 +132,26 @@ const AddCustomer = () => {
}; };
return ( return (
<div className="flex flex-row"> <div className={CLASSES.container}>
<Sidebar /> <Sidebar />
<div className="w-full flex flex-col min-h-screen text-black"> <div className={CLASSES.mainContent}>
<div className="min-h-screen text-gray-800 p-6"> <div className={CLASSES.innerContent}>
<div className="flex justify-between items-center"> <div className={CLASSES.header}>
<div className="flex items-center"> <div className={CLASSES.headerTitle}>
<Link to="/customers" className="text-[#0e355b] text-xl"> <Link to="/customers" className={CLASSES.backButton}>
<FontAwesomeIcon icon={faChevronLeft} /> <FontAwesomeIcon icon={faChevronLeft} />
</Link> </Link>
<h1 className="text-2xl font-semibold text-[#0e355b]"> <h1 className={CLASSES.title}>Add Customer</h1>
Add Customer
</h1>
</div> </div>
<Link to="/CategoryList" className="text-blue-900">Skip for now</Link> <Link to="/CategoryList" className={CLASSES.skipLink}>
Skip for now
</Link>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6"> <div className={CLASSES.formGrid}>
<div className="space-y-6"> <div className={CLASSES.imageSection}>
<div className="text-center"> <div className={CLASSES.imageWrapper}>
<div className="inline-block relative"> <div className={CLASSES.imageContainer}>
<div className="bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden"> <div className={CLASSES.imagePreview}>
{imagePreview ? ( {imagePreview ? (
<img <img
src={imagePreview} src={imagePreview}
@ -109,22 +159,22 @@ const AddCustomer = () => {
className="w-full h-full object-cover" className="w-full h-full object-cover"
/> />
) : ( ) : (
<div className="w-full h-full flex items-center justify-center text-gray-500"> <div className={CLASSES.noImage}>No Image</div>
No Image
</div>
)} )}
</div> </div>
<input <input
type="file" type="file"
accept="image/*" accept="image/*"
className="hidden" className={CLASSES.hiddenInput}
id="imageUpload" id="imageUpload"
onChange={handleImageChange} onChange={handleImageChange}
/> />
<button <button
type="button" type="button"
onClick={() => document.getElementById('imageUpload').click()} onClick={() =>
className="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-gray-700 text-white text-xs px-2 py-1 rounded-full" document.getElementById('imageUpload').click()
}
className={CLASSES.uploadButton}
> >
Upload Upload
</button> </button>
@ -132,60 +182,52 @@ const AddCustomer = () => {
</div> </div>
<div> <div>
<Link to="/add-customer"> <Link to="/add-customer">
<h2 className="text-lg font-semibold">Customer Information</h2> <h2 className={CLASSES.linkTitle}>Customer Information</h2>
</Link> </Link>
<Link to="/measurements"> <Link to="/measurements">
<h2 className="text-lg font-semibold">Measurements</h2> <h2 className={CLASSES.linkTitle}>Measurements</h2>
</Link> </Link>
</div> </div>
</div> </div>
<div className="md:col-span-2 space-y-4"> <div className={CLASSES.formSection}>
<form onSubmit={handleAddCustomerSubmit}> <form onSubmit={handleAddCustomerSubmit}>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>First Name:</label>
First Name:
</label>
<input <input
type="text" type="text"
name="firstname" name="firstname"
value={customerData.firstname} value={customerData.firstname}
onChange={handleInputChange} onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formInput}
/> />
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>Last Name:</label>
Last Name:
</label>
<input <input
type="text" type="text"
name="lastname" name="lastname"
value={customerData.lastname} value={customerData.lastname}
onChange={handleInputChange} onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formInput}
/> />
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>Age:</label>
Age:
</label>
<input <input
type="number" type="number"
name="age" name="age"
value={customerData.age} value={customerData.age}
onChange={handleInputChange} onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formInput}
/> />
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>Gender:</label>
Gender:
</label>
<select <select
name="gender" name="gender"
value={customerData.gender} value={customerData.gender}
onChange={handleInputChange} onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formSelect}
> >
<option value="" disabled> <option value="" disabled>
Select Gender Select Gender
@ -196,74 +238,62 @@ const AddCustomer = () => {
</select> </select>
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>Email:</label>
Email:
</label>
<input <input
type="email" type="email"
name="email" name="email"
value={customerData.email} value={customerData.email}
onChange={handleInputChange} onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formInput}
/> />
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>Address Line 1:</label>
Address Line 1:
</label>
<input <input
type="text" type="text"
name="address_line_1" name="address_line_1"
value={customerData.address_line_1} value={customerData.address.address_line_1}
onChange={handleInputChange} onChange={handleAddressChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formInput}
/> />
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>Address Line 2:</label>
Address Line 2:
</label>
<input <input
type="text" type="text"
name="address_line_2" name="address_line_2"
value={customerData.address_line_2} value={customerData.address.address_line_2}
onChange={handleInputChange} onChange={handleAddressChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formInput}
/> />
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>Nearby Landmark:</label>
Nearby Landmark:
</label>
<input <input
type="text" type="text"
name="nearby_landmark" name="nearby_landmark"
value={customerData.nearby_landmark} value={customerData.address.nearby_landmark}
onChange={handleInputChange} onChange={handleAddressChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formInput}
/> />
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>Pincode:</label>
Pincode:
</label>
<input <input
type="text" type="text"
name="pincode" name="pincode"
value={customerData.pincode} value={customerData.address.pincode}
onChange={handleInputChange} onChange={handleAddressChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formInput}
/> />
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>City:</label>
City:
</label>
<select <select
name="city_id" name="city_id"
value={customerData.city_id} value={customerData.address.city_id}
onChange={handleInputChange} onChange={handleAddressChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formSelect}
> >
<option value="" disabled> <option value="" disabled>
Select City Select City
@ -277,22 +307,17 @@ const AddCustomer = () => {
</select> </select>
</div> </div>
<div> <div>
<label className="block text-gray-800 font-medium"> <label className={CLASSES.formGroup}>Mobile:</label>
Mobile:
</label>
<input <input
type="tel" type="tel"
name="mobile" name="mobile"
value={customerData.mobile} value={customerData.mobile}
onChange={handleInputChange} onChange={handleInputChange}
className="w-full p-2 border-b border-gray-400 focus:border-gray-700" className={CLASSES.formInput}
/> />
</div> </div>
<div className="flex justify-end"> <div className="flex justify-end">
<button <button type="submit" className={CLASSES.submitButton}>
type="submit"
className="bg-[#0e355b] text-white py-2 px-4 rounded-md"
>
Add Customer Add Customer
</button> </button>
</div> </div>

View File

@ -9,14 +9,14 @@ const CustomerList = () => {
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
// Fetch the customers data from the JSON file
getCustomers(); getCustomers();
}, []); }, []);
const getCustomers = async () => { const getCustomers = async () => {
try { try {
const response = await axiosInstance.get('http://localhost:5000/customers'); const response = await axiosInstance.get('http://localhost:5000/customers');
let customers_data = response.data; // List of customers let customers_data = response.data;
setCustomers(customers_data); setCustomers(customers_data);
} catch (error) { } catch (error) {
throw error; throw error;
@ -32,7 +32,7 @@ const CustomerList = () => {
<Sidebar /> <Sidebar />
<div className="flex-grow p-6 bg-gray-100"> <div className="flex-grow p-6 bg-gray-100">
<div className="bg-white p-6 rounded-lg shadow"> <div className="bg-white p-6 rounded-lg shadow">
<div className="flex justify-between items-center mb-6"> <div className="flex justify-between items-center ">
<h1 className="text-2xl font-semibold text-gray-800">customers list</h1> <h1 className="text-2xl font-semibold text-gray-800">customers list</h1>
<button <button
className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md text-sm" className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md text-sm"
@ -41,9 +41,9 @@ const CustomerList = () => {
add new customer add new customer
</button> </button>
</div> </div>
<div className="flex items-center mb-4"> <div className="flex items-center ">
<svg <svg
className="w-5 h-5 mr-2 text-gray-500" className="w-5 h-5 text-gray-500"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -58,7 +58,7 @@ const CustomerList = () => {
</svg> </svg>
<span className="text-sm text-gray-600">filter</span> <span className="text-sm text-gray-600">filter</span>
</div> </div>
<p className="text-sm text-gray-600 mb-4">total customers ({customers.length})</p> <p className="text-sm text-gray-600 ">total customers ({customers.length})</p>
<table className="w-full bg-white"> <table className="w-full bg-white">
<thead> <thead>
<tr className="text-left text-xs uppercase text-gray-600 border-b"> <tr className="text-left text-xs uppercase text-gray-600 border-b">

View File

@ -1,77 +1,97 @@
/* AddCustomer.css */
.container { .container {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
} }
.main-content { .main-content {
flex: 1; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 100vh; min-height: 100vh;
color: #e8e8e8; color: black;
}
.inner-content {
min-height: 100vh;
color: #4a5568; /* text-gray-800 */
padding: 1.5rem; /* p-6 */
} }
.header { .header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 1.5rem;
} }
.header-title { .header-title {
display: flex; display: flex;
align-items: center; align-items: center;
margin-bottom: 1.5rem;
} }
.back-link { .back-button {
color: #0e355b; color: #0e355b;
font-size: 1.25rem; font-size: 1.25rem; /* text-xl */
margin-right: 1rem;
} }
.page-title { .title {
font-size: 1.5rem; font-size: 1.5rem; /* text-2xl */
font-weight: 600; font-weight: 600; /* font-semibold */
color: #0e355b; color: #0e355b;
} }
.skip-link { .skip-link {
color: #0e355b; color: #1e3a8a; /* text-blue-900 */
} }
.sidebar-content { .form-grid {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 1.5rem; /* gap-6 */
}
@media (min-width: 768px) {
.form-grid {
grid-template-columns: repeat(3, 1fr); /* md:grid-cols-3 */
}
}
.image-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1.5rem; gap: 1.5rem; /* space-y-6 */
} }
.image-upload-container { .image-wrapper {
text-align: center;
}
.image-container {
display: inline-block;
position: relative; position: relative;
width: 6rem;
height: 6rem;
margin: 0 auto;
} }
.image-preview { .image-preview {
background-color: #d1d5db; background-color: #d1d5db; /* bg-gray-300 */
width: 100%; width: 6rem; /* w-24 */
height: 100%; height: 6rem; /* h-24 */
border-radius: 9999px; border-radius: 50%;
margin: 0 auto; /* mx-auto */
overflow: hidden; overflow: hidden;
} }
.image-preview img { .no-image {
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover;
}
.no-image {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #6b7280; color: #6b7280; /* text-gray-500 */
}
.hidden-input {
display: none;
} }
.upload-button { .upload-button {
@ -79,57 +99,50 @@
bottom: 0; bottom: 0;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
background-color: #0e355b; background-color: #4b5563; /* bg-gray-700 */
color: #fff; color: white;
font-size: 0.75rem; font-size: 0.75rem; /* text-xs */
padding: 0.5rem; padding: 0.25rem 0.5rem;
border-radius: 9999px; border-radius: 9999px; /* rounded-full */
} }
.section-title { .link-title {
font-size: 1.125rem; font-size: 1.125rem; /* text-lg */
font-weight: 600; font-weight: 600; /* font-semibold */
}
.form-section {
display: flex;
flex-direction: column;
gap: 1rem; /* space-y-4 */
} }
.form-group { .form-group {
margin-bottom: 1rem; color: #4a5568; /* text-gray-800 */
display: flex; font-weight: 500; /* font-medium */
flex-direction: column;
} }
.form-label { .form-input {
display: underline; width: 100%;
color: #4b5563; padding: 0.5rem;
font-weight: 500; border-bottom: 1px solid #cbd5e0; /* border-gray-400 */
&:focus {
border-color: #2d3748; /* border-gray-700 */
}
} }
.form-input,
.form-select { .form-select {
width: 100%; width: 100%;
padding: 0.5rem; padding: 0.5rem;
border: 1px solid #d1d5db; border-bottom: 1px solid #cbd5e0; /* border-gray-400 */
border-radius: 0.375rem; &:focus {
} border-color: #2d3748; /* border-gray-700 */
}
.form-input:focus,
.form-select:focus {
border-color: #e8e8e8;
} }
.submit-button { .submit-button {
background-color: #0e355b; background-color: #0e355b;
color: #d1d5db; color: white;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
border-radius: 0.375rem; border-radius: 0.375rem; /* rounded-md */
align-self: flex-end;
}
.form-row {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
.form-row .form-group {
flex: 1;
} }

20
src/styles/classNames.js Normal file
View File

@ -0,0 +1,20 @@
export const styles = {
container: 'flex flex-row',
content: 'w-full flex flex-col min-h-screen text-black',
header: 'min-h-screen text-secondary p-6 bg-light',
backButton: 'text-primary text-xl',
title: 'text-2xl font-semibold text-primary',
imageWrapper: 'bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden',
imagePreview: 'w-full h-full object-cover',
noImageText: 'w-full h-full flex items-center justify-center text-gray-500',
imageUploadButton: 'image-upload-btn',
form: 'space-y-4',
input: 'input-underline w-full p-2 focus-border',
button: 'btn-primary',
formGroup: 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4',
centerText: 'text-center',
};

8180
yarn.lock

File diff suppressed because it is too large Load Diff