reset password request authentication and route to the login page
This commit is contained in:
parent
89d5b17dc1
commit
fc73ddefca
15826
package-lock.json
generated
15826
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -16,6 +16,7 @@
|
||||
"proxy": "http://localhost:5000",
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.3.7",
|
||||
"@craco/craco": "^7.1.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
@ -30,14 +31,14 @@
|
||||
"@types/react": "^18.2.66",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"autoprefixer": "^9.8.8",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"json-server": "^1.0.0-beta.1",
|
||||
"postcss": "^8.4.39",
|
||||
"tailwindcss": "^3.4.6",
|
||||
"postcss": "^7.0.39",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
|
||||
"vite": "^5.2.0"
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import React, { useContext } from 'react';
|
||||
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
|
||||
import { Layout } from 'antd';
|
||||
import LoginPage from './components/Auth/LoginPage';
|
||||
import ResetPasswordRequest from './components/Auth/ResetPasswordRequest';
|
||||
import AdminDashboard from './components/admin/AdminDashboard';
|
||||
import EmployeeDashboard from './components/employee/EmployeeDashboard';
|
||||
import CustomerList from './components/customer/CustomerList';
|
||||
@ -25,6 +26,7 @@ const App = () => {
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route exact path="/" element={<LoginPage />} />
|
||||
<Route path="/reset-password" element={<ResetPasswordRequest />} />
|
||||
<Route path="/employee/home" element={<EmployeeDashboard />} /> {/* Add HomePage route */}
|
||||
<Route path="/admin" element={<PrivateRoute><AdminDashboard /></PrivateRoute>} />
|
||||
<Route path="/employee" element={<PrivateRoute><EmployeeDashboard /></PrivateRoute>} />
|
||||
|
@ -8,6 +8,7 @@ body, html {
|
||||
/* color: #fff; */
|
||||
}
|
||||
|
||||
|
||||
.login-page {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -39,6 +40,12 @@ body, html {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: block;
|
||||
margin: 0 auto 20px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-top: 0;
|
||||
color: #0e355b;
|
||||
|
@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import axiosInstance from '../../api/axiosConfig';
|
||||
import './LoginPage.css';
|
||||
import BrookslogoIcon from '../../assets/thob-data/BrookslogoIcon.svg';
|
||||
|
||||
const LoginPage = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
@ -12,7 +13,7 @@ const LoginPage = () => {
|
||||
const handleLogin = async (e) => {
|
||||
e.preventDefault();
|
||||
setError('');
|
||||
|
||||
|
||||
try {
|
||||
const response = await axiosInstance.get('http://localhost:5000/authlogin');
|
||||
const user = response.data.find(user => user.email === email && user.password === password);
|
||||
@ -39,11 +40,12 @@ const LoginPage = () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<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="w-full">
|
||||
<img src={BrookslogoIcon} alt="Logo" className="logo" />
|
||||
<h1>welcome back</h1>
|
||||
<p>be among the first to experience 3D magic! register for private alpha.</p>
|
||||
{error && <p className="error-message">{error}</p>}
|
||||
@ -71,7 +73,7 @@ const LoginPage = () => {
|
||||
className="input-field"
|
||||
/>
|
||||
<div className="forgot-password-container">
|
||||
<a href="#">Forgot password?</a>
|
||||
<a href="/reset-password">Forgot password?</a>
|
||||
</div>
|
||||
<button type="submit" className="login-button">
|
||||
login
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import axiosInstance from '../../api/axiosConfig';
|
||||
import './ResetPasswordRequest.css';
|
||||
|
||||
|
||||
const ResetPasswordRequest = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
|
@ -4,9 +4,9 @@ import Sidebar from '../sidebar/Sidebar';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import axiosInstance from '../../api/axiosConfig';
|
||||
import { styles } from '../../styles/classNames';
|
||||
import '../../styles/AddEmployee.css';
|
||||
|
||||
|
||||
const AddEmployee = () => {
|
||||
const navigate = useNavigate();
|
||||
const [employeeData, setEmployeeData] = useState({
|
||||
@ -15,11 +15,13 @@ const AddEmployee = () => {
|
||||
age: '',
|
||||
gender: '',
|
||||
email: '',
|
||||
address_line_1: '',
|
||||
address_line_2: '',
|
||||
nearby_landmark: '',
|
||||
pincode: '',
|
||||
city_id: '',
|
||||
address: {
|
||||
address_line_1: '',
|
||||
address_line_2: '',
|
||||
nearby_landmark: '',
|
||||
pincode: '',
|
||||
city_id: '',
|
||||
},
|
||||
mobile: '',
|
||||
image: null,
|
||||
});
|
||||
@ -83,31 +85,31 @@ const AddEmployee = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-row">
|
||||
<div className={styles.container}>
|
||||
<Sidebar />
|
||||
<div className="w-full flex flex-col min-h-screen text-black">
|
||||
<div className="min-h-screen text-secondary p-6 bg-light">
|
||||
<div className={styles.content}>
|
||||
<div className={styles.header}>
|
||||
<div className="flex items-center ">
|
||||
<Link to="/employees" className="text-primary text-xl ">
|
||||
<Link to="/employees" className={styles.backButton}>
|
||||
<FontAwesomeIcon icon={faChevronLeft} />
|
||||
</Link>
|
||||
<h1 className="text-2xl font-semibold text-primary">
|
||||
<h1 className={styles.title}>
|
||||
create employee account
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<div className="space-y-6 w-full max-w-4xl">
|
||||
<div className="text-center">
|
||||
<div className={`${styles.centerText} space-y-6 w-full max-w-4xl`}>
|
||||
<div className={styles.centerText}>
|
||||
<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 ? (
|
||||
<img
|
||||
src={imagePreview}
|
||||
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
|
||||
</div>
|
||||
)}
|
||||
@ -122,14 +124,14 @@ const AddEmployee = () => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => document.getElementById('imageUpload').click()}
|
||||
className="image-upload-btn"
|
||||
className={styles.imageUploadButton}
|
||||
>
|
||||
Upload
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<form onSubmit={handleAddEmployeeSubmit} className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<form onSubmit={handleAddEmployeeSubmit} className={styles.form}>
|
||||
<div className={styles.formGroup}>
|
||||
<div>
|
||||
<label className="block text-secondary font-medium">
|
||||
employee firstname:
|
||||
@ -139,7 +141,7 @@ const AddEmployee = () => {
|
||||
name="firstname"
|
||||
value={employeeData.firstname}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@ -151,7 +153,7 @@ const AddEmployee = () => {
|
||||
name="lastname"
|
||||
value={employeeData.lastname}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@ -163,7 +165,7 @@ const AddEmployee = () => {
|
||||
name="age"
|
||||
value={employeeData.age}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@ -174,7 +176,7 @@ const AddEmployee = () => {
|
||||
name="gender"
|
||||
value={employeeData.gender}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
>
|
||||
<option value="" disabled>
|
||||
select gender
|
||||
@ -193,7 +195,7 @@ const AddEmployee = () => {
|
||||
name="mobile"
|
||||
value={employeeData.mobile}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2 lg:col-span-3">
|
||||
@ -205,7 +207,7 @@ const AddEmployee = () => {
|
||||
name="address_line_1"
|
||||
value={employeeData.address_line_1}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2 lg:col-span-3">
|
||||
@ -217,7 +219,7 @@ const AddEmployee = () => {
|
||||
name="address_line_2"
|
||||
value={employeeData.address_line_2}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2 lg:col-span-3">
|
||||
@ -229,7 +231,7 @@ const AddEmployee = () => {
|
||||
name="nearby_landmark"
|
||||
value={employeeData.nearby_landmark}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2 lg:col-span-3">
|
||||
@ -241,7 +243,7 @@ const AddEmployee = () => {
|
||||
name="pincode"
|
||||
value={employeeData.pincode}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
<div className="md:col-span-2 lg:col-span-3">
|
||||
@ -252,7 +254,7 @@ const AddEmployee = () => {
|
||||
name="city_id"
|
||||
value={employeeData.city_id}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
>
|
||||
<option value="" disabled>
|
||||
select city
|
||||
@ -274,14 +276,14 @@ const AddEmployee = () => {
|
||||
name="email"
|
||||
value={employeeData.email}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 focus-border"
|
||||
className={styles.input}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-center mt-6">
|
||||
<div className={`${styles.centerText} mt-6`}>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn-primary"
|
||||
className={styles.button}
|
||||
>
|
||||
create account
|
||||
</button>
|
||||
|
@ -5,6 +5,32 @@ import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import Sidebar from '../sidebar/Sidebar';
|
||||
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 navigate = useNavigate();
|
||||
const [customerData, setCustomerData] = useState({
|
||||
@ -13,14 +39,17 @@ const AddCustomer = () => {
|
||||
age: '',
|
||||
gender: '',
|
||||
email: '',
|
||||
address_line_1: '',
|
||||
address_line_2: '',
|
||||
nearby_landmark: '',
|
||||
pincode: '',
|
||||
city_id: '',
|
||||
mobile: '',
|
||||
image: null,
|
||||
address: {
|
||||
address_line_1: '',
|
||||
address_line_2: '',
|
||||
nearby_landmark: '',
|
||||
pincode: '',
|
||||
city_id: '',
|
||||
},
|
||||
});
|
||||
|
||||
const [cities, setCities] = useState([]);
|
||||
const [imagePreview, setImagePreview] = useState(null);
|
||||
|
||||
@ -43,10 +72,21 @@ const AddCustomer = () => {
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setCustomerData({
|
||||
...customerData,
|
||||
setCustomerData((prevData) => ({
|
||||
...prevData,
|
||||
[name]: value,
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
const handleAddressChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setCustomerData((prevData) => ({
|
||||
...prevData,
|
||||
address: {
|
||||
...prevData.address,
|
||||
[name]: value,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
const handleImageChange = (e) => {
|
||||
@ -64,16 +104,26 @@ const AddCustomer = () => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData();
|
||||
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 {
|
||||
const response = await axiosInstance.post('http://localhost:5000/customers', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
console.log('Raw response:', response);
|
||||
const response = await axiosInstance.post(
|
||||
'http://localhost:5000/customers',
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
}
|
||||
);
|
||||
console.log('Raw response:', response);
|
||||
console.log('Customer added successfully:', response.data);
|
||||
navigate('/customers');
|
||||
} catch (error) {
|
||||
@ -82,26 +132,26 @@ const AddCustomer = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-row">
|
||||
<div className={CLASSES.container}>
|
||||
<Sidebar />
|
||||
<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">
|
||||
<div className="flex items-center">
|
||||
<Link to="/customers" className="text-[#0e355b] text-xl">
|
||||
<div className={CLASSES.mainContent}>
|
||||
<div className={CLASSES.innerContent}>
|
||||
<div className={CLASSES.header}>
|
||||
<div className={CLASSES.headerTitle}>
|
||||
<Link to="/customers" className={CLASSES.backButton}>
|
||||
<FontAwesomeIcon icon={faChevronLeft} />
|
||||
</Link>
|
||||
<h1 className="text-2xl font-semibold text-[#0e355b]">
|
||||
Add Customer
|
||||
</h1>
|
||||
<h1 className={CLASSES.title}>Add Customer</h1>
|
||||
</div>
|
||||
<Link to="/CategoryList" className="text-blue-900">Skip for now</Link>
|
||||
<Link to="/CategoryList" className={CLASSES.skipLink}>
|
||||
Skip for now
|
||||
</Link>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<div className="inline-block relative">
|
||||
<div className="bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden">
|
||||
<div className={CLASSES.formGrid}>
|
||||
<div className={CLASSES.imageSection}>
|
||||
<div className={CLASSES.imageWrapper}>
|
||||
<div className={CLASSES.imageContainer}>
|
||||
<div className={CLASSES.imagePreview}>
|
||||
{imagePreview ? (
|
||||
<img
|
||||
src={imagePreview}
|
||||
@ -109,22 +159,22 @@ const AddCustomer = () => {
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center text-gray-500">
|
||||
No Image
|
||||
</div>
|
||||
<div className={CLASSES.noImage}>No Image</div>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
className={CLASSES.hiddenInput}
|
||||
id="imageUpload"
|
||||
onChange={handleImageChange}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => document.getElementById('imageUpload').click()}
|
||||
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"
|
||||
onClick={() =>
|
||||
document.getElementById('imageUpload').click()
|
||||
}
|
||||
className={CLASSES.uploadButton}
|
||||
>
|
||||
Upload
|
||||
</button>
|
||||
@ -132,60 +182,52 @@ const AddCustomer = () => {
|
||||
</div>
|
||||
<div>
|
||||
<Link to="/add-customer">
|
||||
<h2 className="text-lg font-semibold">Customer Information</h2>
|
||||
<h2 className={CLASSES.linkTitle}>Customer Information</h2>
|
||||
</Link>
|
||||
<Link to="/measurements">
|
||||
<h2 className="text-lg font-semibold">Measurements</h2>
|
||||
<h2 className={CLASSES.linkTitle}>Measurements</h2>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="md:col-span-2 space-y-4">
|
||||
<div className={CLASSES.formSection}>
|
||||
<form onSubmit={handleAddCustomerSubmit}>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
First Name:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>First Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="firstname"
|
||||
value={customerData.firstname}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
className={CLASSES.formInput}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
Last Name:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>Last Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="lastname"
|
||||
value={customerData.lastname}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
className={CLASSES.formInput}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
Age:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>Age:</label>
|
||||
<input
|
||||
type="number"
|
||||
name="age"
|
||||
value={customerData.age}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
className={CLASSES.formInput}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
Gender:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>Gender:</label>
|
||||
<select
|
||||
name="gender"
|
||||
value={customerData.gender}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
className={CLASSES.formSelect}
|
||||
>
|
||||
<option value="" disabled>
|
||||
Select Gender
|
||||
@ -196,74 +238,62 @@ const AddCustomer = () => {
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
Email:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>Email:</label>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={customerData.email}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
className={CLASSES.formInput}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
Address Line 1:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>Address Line 1:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="address_line_1"
|
||||
value={customerData.address_line_1}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
value={customerData.address.address_line_1}
|
||||
onChange={handleAddressChange}
|
||||
className={CLASSES.formInput}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
Address Line 2:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>Address Line 2:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="address_line_2"
|
||||
value={customerData.address_line_2}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
value={customerData.address.address_line_2}
|
||||
onChange={handleAddressChange}
|
||||
className={CLASSES.formInput}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
Nearby Landmark:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>Nearby Landmark:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="nearby_landmark"
|
||||
value={customerData.nearby_landmark}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
value={customerData.address.nearby_landmark}
|
||||
onChange={handleAddressChange}
|
||||
className={CLASSES.formInput}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
Pincode:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>Pincode:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="pincode"
|
||||
value={customerData.pincode}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
value={customerData.address.pincode}
|
||||
onChange={handleAddressChange}
|
||||
className={CLASSES.formInput}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
City:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>City:</label>
|
||||
<select
|
||||
name="city_id"
|
||||
value={customerData.city_id}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
value={customerData.address.city_id}
|
||||
onChange={handleAddressChange}
|
||||
className={CLASSES.formSelect}
|
||||
>
|
||||
<option value="" disabled>
|
||||
Select City
|
||||
@ -277,22 +307,17 @@ const AddCustomer = () => {
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">
|
||||
Mobile:
|
||||
</label>
|
||||
<label className={CLASSES.formGroup}>Mobile:</label>
|
||||
<input
|
||||
type="tel"
|
||||
name="mobile"
|
||||
value={customerData.mobile}
|
||||
onChange={handleInputChange}
|
||||
className="w-full p-2 border-b border-gray-400 focus:border-gray-700"
|
||||
className={CLASSES.formInput}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
className="bg-[#0e355b] text-white py-2 px-4 rounded-md"
|
||||
>
|
||||
<button type="submit" className={CLASSES.submitButton}>
|
||||
Add Customer
|
||||
</button>
|
||||
</div>
|
||||
|
@ -9,14 +9,14 @@ const CustomerList = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch the customers data from the JSON file
|
||||
|
||||
getCustomers();
|
||||
}, []);
|
||||
|
||||
const getCustomers = async () => {
|
||||
try {
|
||||
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);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
@ -32,7 +32,7 @@ const CustomerList = () => {
|
||||
<Sidebar />
|
||||
<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">
|
||||
<div className="flex justify-between items-center ">
|
||||
<h1 className="text-2xl font-semibold text-gray-800">customers list</h1>
|
||||
<button
|
||||
className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md text-sm"
|
||||
@ -41,9 +41,9 @@ const CustomerList = () => {
|
||||
add new customer
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center mb-4">
|
||||
<div className="flex items-center ">
|
||||
<svg
|
||||
className="w-5 h-5 mr-2 text-gray-500"
|
||||
className="w-5 h-5 text-gray-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
@ -58,7 +58,7 @@ const CustomerList = () => {
|
||||
</svg>
|
||||
<span className="text-sm text-gray-600">filter</span>
|
||||
</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">
|
||||
<thead>
|
||||
<tr className="text-left text-xs uppercase text-gray-600 border-b">
|
||||
|
@ -1,77 +1,97 @@
|
||||
/* AddCustomer.css */
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
color: #e8e8e8;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.inner-content {
|
||||
min-height: 100vh;
|
||||
color: #4a5568; /* text-gray-800 */
|
||||
padding: 1.5rem; /* p-6 */
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
.back-button {
|
||||
color: #0e355b;
|
||||
font-size: 1.25rem;
|
||||
margin-right: 1rem;
|
||||
font-size: 1.25rem; /* text-xl */
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
.title {
|
||||
font-size: 1.5rem; /* text-2xl */
|
||||
font-weight: 600; /* font-semibold */
|
||||
color: #0e355b;
|
||||
}
|
||||
|
||||
.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;
|
||||
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;
|
||||
width: 6rem;
|
||||
height: 6rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.image-preview {
|
||||
background-color: #d1d5db;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 9999px;
|
||||
background-color: #d1d5db; /* bg-gray-300 */
|
||||
width: 6rem; /* w-24 */
|
||||
height: 6rem; /* h-24 */
|
||||
border-radius: 50%;
|
||||
margin: 0 auto; /* mx-auto */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.image-preview img {
|
||||
.no-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.no-image {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #6b7280;
|
||||
color: #6b7280; /* text-gray-500 */
|
||||
}
|
||||
|
||||
.hidden-input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.upload-button {
|
||||
@ -79,57 +99,50 @@
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: #0e355b;
|
||||
color: #fff;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.5rem;
|
||||
border-radius: 9999px;
|
||||
background-color: #4b5563; /* bg-gray-700 */
|
||||
color: white;
|
||||
font-size: 0.75rem; /* text-xs */
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 9999px; /* rounded-full */
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
.link-title {
|
||||
font-size: 1.125rem; /* text-lg */
|
||||
font-weight: 600; /* font-semibold */
|
||||
}
|
||||
|
||||
.form-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem; /* space-y-4 */
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #4a5568; /* text-gray-800 */
|
||||
font-weight: 500; /* font-medium */
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: underline;
|
||||
color: #4b5563;
|
||||
font-weight: 500;
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border-bottom: 1px solid #cbd5e0; /* border-gray-400 */
|
||||
&:focus {
|
||||
border-color: #2d3748; /* border-gray-700 */
|
||||
}
|
||||
}
|
||||
|
||||
.form-input,
|
||||
.form-select {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #d1d5db;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
.form-input:focus,
|
||||
.form-select:focus {
|
||||
border-color: #e8e8e8;
|
||||
border-bottom: 1px solid #cbd5e0; /* border-gray-400 */
|
||||
&:focus {
|
||||
border-color: #2d3748; /* border-gray-700 */
|
||||
}
|
||||
}
|
||||
|
||||
.submit-button {
|
||||
background-color: #0e355b;
|
||||
color: #d1d5db;
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.375rem;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-row .form-group {
|
||||
flex: 1;
|
||||
border-radius: 0.375rem; /* rounded-md */
|
||||
}
|
||||
|
20
src/styles/classNames.js
Normal file
20
src/styles/classNames.js
Normal 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',
|
||||
|
||||
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user