Compare commits

...

19 Commits

Author SHA1 Message Date
Suraj Birewar
0b07b251fc cahnges done at image section 2024-08-06 10:50:05 +05:30
Suraj Birewar
0af402c018 added integration to save the employee data in employeelist page 2024-08-06 09:15:43 +05:30
Suraj Birewar
c2c5ef0ff0 added employeeService in a application 2024-08-05 22:03:32 +05:30
Suraj Birewar
ccf3547404 added custom measurement to the web application 2024-08-05 18:08:02 +05:30
Suraj Birewar
c13fb500f6 added setmeasurements page and standardmeaurement page 2024-08-05 14:30:31 +05:30
Suraj Birewar
04dfce9c3b applied css design 2024-08-03 09:03:06 +05:30
Suraj Birewar
04b3c4e07b added product categories pages to the application 2024-08-01 15:21:35 +05:30
Suraj Birewar
b10f323455 added services in application and create a categorylist page 2024-07-31 22:58:42 +05:30
261cf4cfbd Added new files for API services and common components. 2024-07-29 14:17:08 +05:30
caf8d4abc0 desgin implementation 2024-07-26 14:53:06 +05:30
7a8a31c482 desgin implementation 2024-07-26 14:35:56 +05:30
d83b15b926 added refine integration and tailwind config theme 2024-07-24 23:08:48 +05:30
a8fe2e603a modified package.json because of error occuring 2024-07-23 14:29:01 +05:30
fc73ddefca reset password request authentication and route to the login page 2024-07-23 13:42:51 +05:30
89d5b17dc1 add css files for each compnent 2024-07-22 17:12:01 +05:30
cb7ab37eb0 css and layout implementation 2024-07-22 12:12:41 +05:30
f3eeefe551 changes done in Addemployee page and AddCustomer page 2024-07-19 14:52:54 +05:30
dddd1b7ea2 changes done in Addemployee page and AddCustomer page 2024-07-18 15:14:17 +05:30
8bde35342c 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.
2024-07-18 11:44:46 +05:30
72 changed files with 24887 additions and 2357 deletions

12
.env Normal file
View File

@ -0,0 +1,12 @@
const environment = import.meta.env.VITE_ENVIRONMENT;
const apiUrl = import.meta.env.VITE_API_URL;
console.log('Environment:', environment);
console.log('API URL:', apiUrl);
// Example of using the API URL
fetch(`${apiUrl}/endpoint`)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

209
db.json
View File

@ -11,6 +11,12 @@
"email": "employee@gmail.com",
"password": "123",
"role": "employee"
},
{
"id": "de4c"
},
{
"id": "5996"
}
],
"health-check": {
@ -19,29 +25,83 @@
"admin": [
{
"id": "1",
"name": "Admin User",
"firstname": "Admin User",
"lastname": "last ",
"email": "adminuser@example.com"
}
],
"employee": [
"employees": [
{
"id": "1",
"name": "Employee User",
"firstname": "Employee User",
"lastname": "last ",
"email": "employeeuser@example.com"
},
{
"id": "6b21",
"firstname": "suraj",
"lastname": "birewar",
"email": "surajbirewar001@gmail.com"
},
{
"id": "d010",
"firstname": "gajanan",
"lastname": "bodke",
"email": "gajanan001@gmail.com"
},
{
"id": "4db0",
"firstname": "sachin",
"lastname": "patil",
"age": "22",
"gender": "",
"email": "sachin@gmail.com",
"mobile": "07559393995",
"address": {
"address_line_1": "",
"address_line_2": "",
"nearby_landmark": "",
"pincode": "",
"city_id": ""
},
"image": null,
"address.address_line_1": "Sarovar line 1 , Bavdhan, Pune",
"address.address_line_2": "LOCAL :,Bavdhan",
"address.pincode": "411021",
"address.nearby_landmark": "i",
"address.city_id": "1"
}
],
"products": [
{
"id": "1",
"name": "Product 1",
"category": "Category 1",
"price": 100
"ID": "1",
"name": "Men's T-Shirt",
"sku": "TS-MEN-001-BL-XL",
"short_description": "A comfortable men's clothing fibers",
"long_description": "This men's shirt is made from 100% cotton, providing a soft and comfortable feel. Perfect for casual wear.",
"thumbnail_url": "https://example.com/men-tshirt.jpg",
"category_id": "101",
"id": "d6ce"
},
{
"id": "2",
"name": "Product 2",
"category": "Category 2",
"price": 200
"ID": "2",
"name": "Women's Jacket",
"sku": "JK-WOM-001-RE-M",
"short_description": "Stylish women's jacket",
"long_description": "A trendy women's jacket, perfect for chilly weather. Made with high-quality materials to ensure warmth and comfort.",
"thumbnail_url": "https://example.com/women-jacket.jpg",
"category_id": "102",
"id": "60db"
},
{
"ID": "3",
"name": "Unisex Sneakers",
"sku": "SN-UNI-001-WH-42",
"short_description": "Comfortable unisex tuxiods",
"long_description": "These unisex tuxidos are designed for comfort and style. Ideal for everyday wear.",
"thumbnail_url": "https://example.com/tuxidos.jpg",
"category_id": "103",
"id": "7228"
}
],
"categories": [
@ -57,61 +117,116 @@
"customers": [
{
"id": "1",
"name": "Customer 1",
"firstname": "vitthal",
"lastname": "patil",
"email": "customer1@example.com",
"phone": "1234567890"
},
{
"id": "2",
"name": "Customer 2",
"firstname": "rushi",
"lastname": "shelke",
"email": "customer2@example.com",
"phone": "0987654321"
},
{
"name": "Suraj Birewar",
"age": "24",
"gender": "Male",
"id": "7779",
"firstname": "Suraj",
"lastname": "Birewar",
"age": "22",
"gender": "male",
"email": "surajbirewar001@gmail.com",
"address": "Bavdhan, Pune",
"mobile": "07559393995",
"id": "3"
"address": {
"address_line_1": "Bavdhan, Pune",
"address_line_2": "LOCAL :,Bavdhan",
"nearby_landmark": "sbi",
"pincode": "411021",
"city_id": "1"
},
"image": null
}
],
"cities": [
{
"id": "1",
"name": "New York"
},
{
"name": "rahul",
"age": "21",
"gender": "male",
"email": "rahul001@gmail.com",
"address": "Bavdhan, Pune",
"mobile": "7559393995",
"id": "4"
"id": "2",
"name": "Los Angeles"
},
{
"name": "atharv",
"age": "19",
"gender": "male",
"email": "atharv@gmail.com",
"address": "baner pune ",
"mobile": "784365873",
"id": "5"
"id": "3",
"name": "Chicago"
},
{
"id": "3682",
"name": "dikshant",
"age": "23",
"gender": "male",
"email": "dikshant001@gmail.com",
"address": "Bavdhan, Pune",
"mobile": "45654665645"
"id": "4",
"name": "Houston"
},
{
"id": "377d",
"firstname": "tejas",
"lastname": "chari",
"age": "28",
"gender": "male",
"email": "tejas001@gmail.com",
"address": " Baner, Pune",
"mobile": "8675493099"
"id": "5",
"name": "Phoenix"
},
{
"id": "333e",
"name": "las vegas"
}
],
"users": [
{
"ID": "1",
"Email": "adminuser@example.com",
"Roles": [
"admin"
],
"Profile": {
"ID": "p1",
"FirstName": "Admin",
"LastName": "User",
"Phone": "1234567890",
"Age": 35,
"Gender": "MALE"
},
"CreatedAt": "2023-01-01T10:00:00Z",
"UpdatedAt": "2023-01-01T12:00:00Z",
"id": "2506"
},
{
"ID": "2",
"Email": "employeeuser@example.com",
"Roles": [
"employee"
],
"Profile": {
"ID": "p2",
"FirstName": "Employee",
"LastName": "User",
"Phone": "0987654321",
"Age": 28,
"Gender": "MALE"
},
"CreatedAt": "2023-01-02T10:00:00Z",
"UpdatedAt": "2023-01-02T12:00:00Z",
"id": "bd52"
},
{
"ID": "3",
"Email": "newuser@example.com",
"Roles": [
"customer"
],
"Profile": {
"ID": "p3",
"FirstName": "New",
"LastName": "User",
"Phone": "1122334455",
"Age": 22,
"Gender": "FEMALE"
},
"CreatedAt": "2023-01-03T10:00:00Z",
"UpdatedAt": "2023-01-03T12:00:00Z",
"id": "eefd"
}
]
}

View File

@ -1,7 +1,8 @@
import axios from 'axios';
const axiosInstance = axios.create({
baseURL: 'http://localhost:5000'
baseURL: 'http://localhost:5000',
timeout: 10000,
});
export default axiosInstance;

18202
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,24 +16,45 @@
"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",
"@pankod/refine-simple-rest": "^3.39.0",
"@refinedev/core": "^4.53.0",
"@refinedev/kbar": "^1.3.12",
"@refinedev/mui": "^5.19.0",
"@refinedev/simple-rest": "^5.0.8",
"antd": "^5.18.2",
"axios": "^1.7.2",
"classnames": "^2.5.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.1"
"react-router-dom": "^6.25.1",
"react-toastify": "^10.0.5"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.0.0",
"@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",
"axios-mock-adapter": "^1.22.0",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"jest": "^27.5.1",
"json-server": "^1.0.0-beta.1",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.4",
"vite": "^5.2.0"
"postcss": "^7.0.39",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
"vite": "^5.3.4"
},
"jest": {
"testEnvironment": "jsdom",
"moduleNameMapper": {
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
}
}
}

View File

@ -1,44 +1,81 @@
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 AdminDashboard from './components/admin/AdminDashboard';
import EmployeeDashboard from './components/employee/EmployeeDashboard';
import CustomerList from './components/customer/CustomerList';
import AddCustomer from './components/customer/AddCustomer';
import Sidebar from './components/sidebar/Sidebar';
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 React 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 "./pages/AdminDashboard";
import EmployeeDashboard from "./pages/EmployeeDashboard";
import CustomerList from "./components/customer/CustomerList";
import AddCustomer from "./components/customer/AddCustomer";
import Sidebar from "./components/sidebar/Sidebar";
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";
import CategoryList from "./components/categories/CategoryList";
import ShirtsProductList from "./components/categories/ShirtsProductList";
import SuitsProductList from "./components/categories/SuitsProductList";
import TuxedosProductList from "./components/categories/TuxedosProductList";
import JacketsProductList from "./components/categories/JacketsProductList.jsx";
import Customize from "./components/customize/Customize.jsx";
import SetMeasurements from "./components/customize/SetMeasurements.jsx";
import StandardMeasurements from "./components/customize/StandardMeasurements.jsx";
import CustomMeasurements from "./components/customize/CustomMeasurements.jsx";
import { Refine } from "@refinedev/core";
import { RefineKbarProvider } from "@refinedev/kbar";
import { ToastContainer, toast } from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
const { Content } = Layout;
const App = () => {
const { user } = { name: 'Suraj' }; // useContext(AuthContext);
const { user } = { name: "Suraj" }; // useContext(AuthContext);
return (
<div className="min-h-screen flex w-full">
<Content className="flex-1 text-white">
<Router>
<RefineKbarProvider>
<Refine>
<AuthProvider>
<Router>
<div className="min-h-screen flex w-full">
<Content className="flex-1 text-white">
<Routes>
<Route exact path="/" element={<LoginPage />} />
<Route path="/employee/home" element={<EmployeeDashboard />} /> {/* Add HomePage route */}
<Route path="/reset-password" element={<ResetPasswordRequest />} />
<Route path="/employee/home" element={<EmployeeDashboard />} />
<Route path="/admin" element={<PrivateRoute><AdminDashboard /></PrivateRoute>} />
<Route path="/employee" element={<PrivateRoute><EmployeeDashboard /></PrivateRoute>} />
<Route path="/customers" element={<PrivateRoute><CustomerList /></PrivateRoute>} />
<Route path="/add-customer" element={<PrivateRoute><AddCustomer /></PrivateRoute>} />
<Route path="/employee/create-order" element={<CustomerList />} />
<Route path="/measurements" element={<CustomerMeasurements />} />
<Route path="employees" element={<PrivateRoute><EmployeeList /></PrivateRoute>} />
<Route path="/employees" element={<PrivateRoute><EmployeeList /></PrivateRoute>} />
<Route path="/add-employee" element={<PrivateRoute><AddEmployee /></PrivateRoute>} />
<Route path="/employee-profile" element={<PrivateRoute><EmployeeProfile /></PrivateRoute>} />
<Route path="/CategoryList" element={<PrivateRoute><CategoryList /></PrivateRoute>} />
<Route path="/products/shirts" element={<ShirtsProductList />} />
<Route path="/products/suits" element={<SuitsProductList />} />
<Route path="/products/tuxedos" element={<TuxedosProductList />} />
<Route path="/products/jackets" element={<JacketsProductList />} />
<Route path="/customize/:id" element={<Customize />} />
<Route path="/setmeasurements" element={<SetMeasurements />} />
<Route path="/measurements/standard" element={<StandardMeasurements />} />
<Route path="/measurements/custom" element={<CustomMeasurements />} />
<Route path="/orders" element={<CustomMeasurements />} />
</Routes>
</Router>
</AuthProvider>
</Content>
</div>
</Content>
</div>
</AuthProvider>
</Refine>
</RefineKbarProvider>
<ToastContainer /> {/* Added ToastContainer for displaying notifications */}
</Router>
);
};

View File

@ -1,20 +1,49 @@
import axios from 'axios';
// Determine environment and set baseURL accordingly
const environment = import.meta.env.VITE_ENVIRONMENT;
const baseURL = environment === 'production'
? import.meta.env.VITE_API_URL
: 'http://localhost:5000'; // Default for development
console.log(`Axios baseURL set to: ${baseURL}`); // Log the base URL for debugging
const axiosInstance = axios.create({
baseURL: '/api',
baseURL,
headers: {
'Content-Type': 'application/json',
},
});
// Request interceptor to add JWT token
axiosInstance.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
}, (error) => {
}, error => {
// Handle request error
console.error('Request error:', error);
return Promise.reject(error);
});
// Response interceptor to handle responses globally
axiosInstance.interceptors.response.use(
response => response,
error => {
if (error.response) {
// Server responded with a status other than 2xx
console.error('Error response:', error.response.data);
} else if (error.request) {
// No response received from the server
console.error('Error request:', error.request);
} else {
// Something else caused the error
console.error('Error message:', error.message);
}
return Promise.reject(error);
}
);
export default axiosInstance;

View File

@ -0,0 +1,34 @@
import axiosInstance from '../axiosConfig';
export const getEmployees = async () => {
const response = await axiosInstance.get('http://localhost:5000/employees');
return response.data;
};
export const getEmployeeById = async (id) => {
const response = await axiosInstance.get(`http://localhost:5000/employees/${id}`);
return response.data;
};
export const createEmployee = async (employeeData) => {
const response = await axiosInstance.post('http://localhost:5000/employees', employeeData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
return response.data;
};
export const updateEmployee = async (id, updatedData) => {
const response = await axiosInstance.patch(`http://localhost:5000/employees/${id}`, updatedData);
return response.data;
};
export const deleteEmployee = async (id) => {
await axiosInstance.delete(`http://localhost:5000/employees/${id}`);
};

View File

@ -0,0 +1,71 @@
import axiosMock from 'axios';
import axiosInstance from './api/axiosConfig';
import {
getUsers,
getUserById,
createUser,
updateUser,
deleteUser,
} from './api/userService';
import MockAdapter from 'axios-mock-adapter';
const mock = new MockAdapter(axiosInstance);
describe('userService', () => {
afterEach(() => {
mock.reset();
});
it('should fetch all users', async () => {
const users = [{ id: 1, name: 'John Doe' }, { id: 2, name: 'Jane Doe' }];
mock.onGet('http://localhost:5000/user').reply(200, users);
const result = await getUsers();
expect(result).toEqual(users);
});
it('should fetch a user by ID', async () => {
const user = { id: 1, name: 'John Doe' };
mock.onGet('http://localhost:5000/user/1').reply(200, user);
const result = await getUserById(1);
expect(result).toEqual(user);
});
it('should create a new user', async () => {
const newUser = { name: 'John Doe' };
const createdUser = { id: 1, ...newUser };
mock.onPost('http://localhost:5000/user').reply(201, createdUser);
const result = await createUser(newUser);
expect(result).toEqual(createdUser);
});
it('should update a user by ID', async () => {
const updatedData = { name: 'John Smith' };
const updatedUser = { id: 1, ...updatedData };
mock.onPatch('http://localhost:5000/user/1').reply(200, updatedUser);
const result = await updateUser(1, updatedData);
expect(result).toEqual(updatedUser);
});
it('should delete a user by ID', async () => {
mock.onDelete('http://localhost:5000/user/1').reply(200);
const result = await deleteUser(1);
expect(result).toBeUndefined();
});
it('should handle errors correctly', async () => {
mock.onGet('http://localhost:5000/user').reply(500);
try {
await getUsers();
} catch (error) {
expect(error).toBeTruthy();
}
});
});

52
src/api/userServices.js Normal file
View File

@ -0,0 +1,52 @@
import React, { useState, useEffect } from 'react';
import axiosInstance from '../api/axiosConfig';
export const getUsers = async () => {
try {
const response = await axiosInstance.get('http://localhost:5000/user');
return response.data;
} catch (error) {
console.error('Error fetching users:', error);
throw error;
}
};
export const getUserById = async (id) => {
try {
const response = await axiosInstance.get(`http://localhost:5000/user/${id}`);
return response.data;
} catch (error) {
console.error(`Error fetching user with ID ${id}:`, error);
throw error;
}
};
export const createUser = async (userData) => {
try {
const response = await axiosInstance.post('http://localhost:5000/user', userData);
return response.data;
} catch (error) {
console.error('Error creating user:', error);
throw error;
}
};
export const updateUser = async (id, updatedData) => {
try {
const response = await axiosInstance.patch(`http://localhost:5000/user/${id}`, updatedData);
return response.data;
} catch (error) {
console.error(`Error updating user with ID ${id}:`, error);
throw error;
}
};
export const deleteUser = async (id) => {
try {
const response = await axiosInstance.delete(`http://localhost:5000/user/${id}`);
return response.data;
} catch (error) {
console.error(`Error deleting user with ID ${id}:`, error);
throw error;
}
};

View File

@ -5,7 +5,6 @@ body, html {
padding: 0;
font-family: 'Nunito Sans', sans-serif;
background-color: #ffffff;
/* color: #fff; */
}
.login-page {
@ -15,28 +14,15 @@ body, html {
min-height: 100vh;
padding: 4rem;
background-color: #ffffff;
position: relative;
}
.login-container {
/* background-color: #222; */
border-radius: 8px;
padding: 40px;
box-shadow: 0 0 10px #d1d5db;
width: 310.4px;
display: flex;
flex-direction: column;
row-gap: 28px;
column-gap: 28px;
font-size: 14px;
font-weight: 500;
line-height: 14px;
box-sizing: border-box;
}
.form-container {
display: flex;
flex-direction: column;
align-items: center;
.logo {
position: absolute;
top: 1rem;
left: 1rem;
width: 100px;
height: auto;
}
h1 {
@ -51,44 +37,20 @@ p {
text-align: center;
}
form {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
label {
display: none;
}
/* input[type="email"],
input[type="password"] {
input {
width: 100%;
padding: 12px;
margin-bottom: 20px;
border: 1px solid #B3B3B3;
border-radius: 4px;
font-size: 16px;
background-color: #09090B;
color: #fff;
background-color: #e8e8e8;
color: #4b5563;
box-sizing: border-box;
} */
.login-page input {
width: 100%;
padding: 12px;
margin-bottom: 20px;
border: 1px solid #B3B3B3; /* Darker border */
border-radius: 4px;
font-size: 16px;
background-color: #e8e8e8; /* Dark background for input fields */
color: #4b5563; /* White text for input fields */
}
input[type="email"]:focus,
input[type="password"]:focus {
border-color: #4b5563;
input:focus {
border-color: #0e355b;
outline: none;
}
@ -105,10 +67,6 @@ input[type="password"]:focus {
text-decoration: none;
}
/* .forgot-password-container a:hover {
text-decoration: underline;
} */
.login-button {
width: 100%;
padding: 15px 30px;
@ -123,15 +81,3 @@ input[type="password"]:focus {
.login-button:hover {
background-color: #154676;
}
.login-page input:focus {
border-color: #0e355b; /* Orange border on focus */
outline: none;
}
.login-page a {
color: #4b5563;
text-decoration: none;
margin-bottom: 20px;
display: inline-block;
}

View File

@ -1,7 +1,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,19 +12,19 @@ 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);
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');
@ -39,44 +39,44 @@ 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">
<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>}
<form onSubmit={handleLogin} 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="john123@domain.com"
required
className="input-field"
/>
<label htmlFor="password" className="block"></label>
<input
type="password"
id="password"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter your password"
required
className="input-field"
/>
<div className="forgot-password-container">
<a href="#">Forgot password?</a>
</div>
<button type="submit" className="login-button">
Login
</button>
</form>
<div className="flex flex-col min-h-screen bg-secondary p-4 relative">
<img src={BrookslogoIcon} alt="Logo" className="absolute top-6 left-6 w-24" />
<div className="flex flex-col items-center justify-center flex-1 px-4 py-6">
<h1 className="text-primary-text text-2xl font-bold mb-4">welcome back</h1>
<p className="text-secondary-text text-center mb-6">be among the first to experience 3D magic! Register for private alpha.</p>
{error && <p className="text-red-500 text-center mb-4">{error}</p>}
<div className="flex flex-col space-y-4 w-full max-w-sm">
<input
type="email"
id="email"
name="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="john123@domain.com"
required
className="w-full p-3 border border-gray-300 rounded-md text-gray-700 focus:border-primary focus:outline-none"
/>
<input
type="password"
id="password"
name="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Enter your password"
required
className="w-full p-3 border border-gray-300 rounded-md text-gray-700 focus:border-primary focus:outline-none"
/>
<div className="flex justify-end">
<a href="/reset-password" className="text-primary-text">forgot password?</a>
</div>
<button
onClick={handleLogin}
className="w-full py-3 bg-primary text-button-text rounded-md text-lg font-semibold hover:bg-primary-dark"
>
login
</button>
</div>
</div>
</div>

View File

@ -0,0 +1,49 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import axiosInstance from '../../api/axiosConfig';
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

@ -0,0 +1,12 @@
import React from "react";
import { toast } from "react-toastify";
const ExampleComponent = () => {
const handleClick = () => {
toast.success("This is a success notification!");
};
return <button onClick={handleClick}>Show Notification</button>;
};
export default ExampleComponent;

View File

@ -1,29 +1,87 @@
import React, { useEffect, useState } from 'react';
import axiosInstance from '../api/axiosInstance';
import { getUsers, createUser, updateUser, deleteUser } from '../api/userService';
const UsersList = () => {
const UserList = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [newUser, setNewUser] = useState({ email: '', roles: '' });
useEffect(() => {
axiosInstance.get('/users')
.then(response => {
setUsers(response.data);
})
.catch(error => {
console.error('There was an error fetching the users!', error);
});
const fetchUsers = async () => {
try {
const userData = await getUsers();
setUsers(userData);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
const handleCreateUser = async () => {
try {
const createdUser = await createUser(newUser);
setUsers([...users, createdUser]);
setNewUser({ email: '', roles: '' });
} catch (error) {
setError(error);
}
};
const handleUpdateUser = async (userId, updatedData) => {
try {
const updatedUser = await updateUser(userId, updatedData);
setUsers(users.map((user) => (user.ID === userId ? updatedUser : user)));
} catch (error) {
setError(error);
}
};
const handleDeleteUser = async (userId) => {
try {
await deleteUser(userId);
setUsers(users.filter((user) => user.ID !== userId));
} catch (error) {
setError(error);
}
};
if (loading) return <p>Loading...</p>;
if (error) return <p>Error loading users: {error.message}</p>;
return (
<div>
<h1>Users List</h1>
<h2>User List</h2>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
{users.map((user) => (
<li key={user.ID}>
{user.Email} - {user.Roles.join(', ')}
<button onClick={() => handleUpdateUser(user.ID, {/* Updated data */})}>Update</button>
<button onClick={() => handleDeleteUser(user.ID)}>Delete</button>
</li>
))}
</ul>
<h3>Add New User</h3>
<input
type="email"
placeholder="Email"
value={newUser.email}
onChange={(e) => setNewUser({ ...newUser, email: e.target.value })}
/>
<input
type="text"
placeholder="Roles (comma-separated)"
value={newUser.roles}
onChange={(e) => setNewUser({ ...newUser, roles: e.target.value.split(',') })}
/>
<button onClick={handleCreateUser}>Create User</button>
</div>
);
};
export default UsersList;
export default UserList;

View File

@ -1,7 +1,33 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig';
import { createEmployee } from "../../services_1/employeeService";
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]',
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',
formSection: 'md:col-span-2 space-y-4',
formGroup: 'text-gray-800 font-medium',
formInput: 'p-2 border-b border-gray-400 focus:border-gray-700',
formSelect: 'p-2 border-b border-gray-400 focus:border-gray-700',
submitButton: 'bg-[#0e355b] text-white py-2 px-4 rounded-md',
};
const AddEmployee = () => {
const navigate = useNavigate();
@ -11,22 +37,65 @@ const AddEmployee = () => {
age: '',
gender: '',
email: '',
address: '',
mobile: '',
address: {
address_line_1: '',
address_line_2: '',
nearby_landmark: '',
pincode: '',
city_id: '',
},
image: null,
});
const [cities, setCities] = useState([]);
const [imagePreview, setImagePreview] = useState(null);
useEffect(() => {
const fetchCities = async () => {
try {
const response = await axiosInstance.get('/cities');
if (Array.isArray(response.data)) {
setCities(response.data);
} else {
console.error('Error: Data is not an array', response.data);
}
} catch (error) {
console.error('Error fetching cities:', error.response ? error.response.data : error.message);
}
};
fetchCities();
}, []);
const handleInputChange = (e) => {
const { name, value } = e.target;
setEmployeeData({
...employeeData,
setEmployeeData((prevData) => ({
...prevData,
[name]: value,
});
}));
};
const handleImageChange = (e) => {
const file = e.target.files[0];
if (file) {
setEmployeeData({
...employeeData,
image: file,
});
setImagePreview(URL.createObjectURL(file));
}
};
const handleAddEmployeeSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
Object.keys(employeeData).forEach((key) => {
formData.append(key, employeeData[key]);
});
try {
await axiosInstance.post('/employees', employeeData);
const data = await createEmployee(employeeData);
console.log('Employee added successfully:', data);
navigate('/employees');
} catch (error) {
console.error('Error adding employee:', error.response || error.message);
@ -34,107 +103,170 @@ const AddEmployee = () => {
};
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 mb-6">
<h1 className="text-2xl font-semibold text-blue-900">Creating Employee Account</h1>
<Link to="/" className="text-blue-900">Skip for now</Link>
<div className={CLASSES.mainContent}>
<div className={CLASSES.innerContent}>
<div className={CLASSES.header}>
<div className={CLASSES.headerTitle}>
<Link to="/employees" className={CLASSES.backButton}>
<FontAwesomeIcon icon={faChevronLeft} />
</Link>
<h1 className={CLASSES.title}>create employee account</h1>
</div>
</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"></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
<div className={CLASSES.formGrid}>
<div className={CLASSES.imageSection}>
<div className={CLASSES.imageWrapper}>
<div className={CLASSES.imageContainer}>
<div className={CLASSES.imagePreview}>
{imagePreview ? (
<img
src={imagePreview}
alt="Employee"
className="w-full h-full object-cover"
/>
) : (
<div className={CLASSES.noImage}>no image</div>
)}
</div>
<input
type="file"
accept="image/*"
className={CLASSES.hiddenInput}
id="imageUpload"
onChange={handleImageChange}
/>
<button
type="button"
onClick={() => document.getElementById('imageUpload').click()}
className={CLASSES.uploadButton}
>
</button>
</div>
</div>
</div>
<div className="md:col-span-2 space-y-4">
<div className={CLASSES.formSection}>
<form onSubmit={handleAddEmployeeSubmit}>
<div>
<label className="block text-gray-800 font-medium">Employee Firstname:</label>
<label className={CLASSES.formGroup}>first name:</label>
<input
type="text"
name="firstname"
value={employeeData.firstname}
onChange={handleInputChange}
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">Employee Lastname:</label>
<label className={CLASSES.formGroup}>last name:</label>
<input
type="text"
name="lastname"
value={employeeData.lastname}
onChange={handleInputChange}
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
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={employeeData.age}
onChange={handleInputChange}
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
className={CLASSES.formInput}
/>
</div>
<div>
<label className="block text-gray-800 font-medium">Gender:</label>
<select
name="gender"
value={employeeData.gender}
onChange={handleInputChange}
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>
</select>
</div>
<div>
<label className="block text-gray-800 font-medium">Email:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
className={CLASSES.formInput}
value={employeeData.email}
onChange={handleInputChange}
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
/>
</div>
<div>
<label className="block text-gray-800 font-medium">Address:</label>
<input
type="text"
name="address"
value={employeeData.address}
onChange={handleInputChange}
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
/>
</div>
<div>
<label className="block text-gray-800 font-medium">Mobile No:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="mobile">Mobile Number</label>
<input
type="text"
id="mobile"
name="mobile"
className={CLASSES.formInput}
value={employeeData.mobile}
onChange={handleInputChange}
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
/>
</div>
<br />
<div className="text-center">
<button type="submit" className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md font-semibold">
Create Account
</button>
<div className={CLASSES.formGroup}>
<label htmlFor="address_line_1">Address Line 1</label>
<input
type="text"
id="address_line_1"
name="address.address_line_1"
className={CLASSES.formInput}
value={employeeData.address.address_line_1}
onChange={handleInputChange}
/>
</div>
<div className={CLASSES.formGroup}>
<label htmlFor="address_line_2">Address Line 2</label>
<input
type="text"
id="address_line_2"
name="address.address_line_2"
className={CLASSES.formInput}
value={employeeData.address.address_line_2}
onChange={handleInputChange}
/>
</div>
<div className={CLASSES.formGroup}>
<label htmlFor="nearby_landmark">Nearby Landmark</label>
<input
type="text"
id="nearby_landmark"
name="address.nearby_landmark"
className={CLASSES.formInput}
value={employeeData.address.nearby_landmark}
onChange={handleInputChange}
/>
</div>
<div className={CLASSES.formGroup}>
<label htmlFor="pincode">Pincode</label>
<input
type="text"
id="pincode"
name="address.pincode"
className={CLASSES.formInput}
value={employeeData.address.pincode}
onChange={handleInputChange}
/>
</div>
<div className={CLASSES.formGroup}>
<label htmlFor="city_id">City</label>
<select
id="city_id"
name="address.city_id"
className={CLASSES.formSelect}
value={employeeData.address.city_id}
onChange={handleInputChange}
>
<option value="">Select City</option>
{cities.map((city) => (
<option key={city.id} value={city.id}>
{city.name}
</option>
))}
</select>
</div>
<button type="submit" className={CLASSES.submitButton}>
add employee
</button>
</form>
</div>
</div>

View File

@ -1,53 +0,0 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar';
import './Dashboard.css';
const AdminDashboard = () => {
const navigate = useNavigate();
const handleClick = (link) => {
navigate(link); // Redirect to the provided link
};
return (
<div className="flex flex-row">
<Sidebar />
<div className="w-full flex flex-col min-h-screen text-white">
<header className="w-full p-4 flex justify-between items-center">
<div className="flex items-center">
<h1 className="text-#0e355b text-2xl font-bold">Admin Dashboard</h1>
</div>
<div>
<p className="text-#4b5563">welcome back (ester howard)</p>
</div>
</header>
<main className="flex-1 flex flex-col items-center justify-center p-6">
<div className="w-full max-w-3xl text-center">
<img src="path_to_image" alt="Dashboard Graphic" className="w-full rounded-md mb-6" />
<button
className="w-full py-4 bg-#0e355b text-#d1d5db rounded-md font-semibold mb-4 hover:bg-#154676"
onClick={() => handleClick('')}
>
view orders
</button>
<button
className="w-full py-4 bg-#0e355b text-#d1d5db rounded-md font-semibold mb-4 hover:bg-#154676"
onClick={() => handleClick('/employees')}
>
view employees
</button>
<button
className="w-full py-4 bg-#0e355b text-#d1d5db rounded-md font-semibold mb-4 hover:bg-#154676"
onClick={() => handleClick('')}
>
view Customers
</button>
</div>
</main>
</div>
</div>
);
};
export default AdminDashboard;

View File

@ -1,89 +0,0 @@
/* Importing Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Nunito+Sans:wght@200;400;600;800&display=swap');
/* General styles */
/* body {
font-family: 'Nunito Sans', sans-serif;
background-color: #09090B;
margin: 0;
padding: 0;
color: #fff;
} */
.dashboard {
padding: 24px;
background-color: #ffffff;
/* color: #; */
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
}
.dashboard-title {
position: absolute;
top: 20px;
left: 20px;
font-size: 24px;
}
.dashboard-content {
position: absolute;
top: 150px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
align-items: center;
width: 80%;
max-width: 800px;
}
.dashboard-image {
width: 100%;
height: auto;
margin-bottom: 40px;
}
.dashboard-buttons {
display: flex;
flex-direction: column;
gap: 20px;
align-items: center;
background-color: #0e355b;
}
.dashboard-button {
background-color: #0e355b;
border-color: #0e355b;
width: 300px;
height: 60px;
font-size: 16px;
}
.dashboard-link {
color: #0e355b;
}
.button:hover {
background-color: #154676;
border-color: #154676;
}
/* Responsive Styles */
@media (max-width: 768px) {
.dashboard-title {
font-size: 20px;
top: 10px;
left: 10px;
}
.dashboard-content {
top: 120px;
width: 90%;
}
.dashboard-button {
width: 100%;
}
}

View File

@ -2,35 +2,33 @@ import React, { useState, useEffect, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig';
// import EmployeeCards from './EmployeeCards';
// import PropTypes from 'prop-types';
const EmployeeList = ({ className = '' }) => {
const [employees, setEmployees] = useState([]);
const navigate = useNavigate();
useEffect(() => {
// Fetch the employees data from the JSON file
getEmployees();
}, []);
const getEmployees = async () => {
try {
const response = await axiosInstance.get('http://localhost:5000/employees');
let employees_data = response.data; // List of employees
let employees_data = response.data;
setEmployees(employees_data);
} catch (error) {
throw error;
console.error('Error fetching employees:', error.response || error.message);
}
};
const handleAddEmployee = () => {
navigate('/add-employee');
};
navigate('/add-employee');
};
// const onEmployeeCardsClick = useCallback(() => {
// navigate('/employee-profile');
// }, [navigate]);
const onEmployeeClick = useCallback(() => {
navigate('/employee-profile');
}, [navigate]);
return (
<div className="flex flex-row min-h-screen">
@ -38,12 +36,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,9 +59,9 @@ 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">
@ -80,12 +78,12 @@ const EmployeeList = ({ className = '' }) => {
<td className="py-2 text-gray-800">{employee.firstname} {employee.lastname}</td>
<td className="py-2 text-gray-800">{employee.email}</td>
<td className="py-2 text-gray-800">
<button
className="text-#4b5563 underline"
// onClick={onEmployeeCardsClick}
<span
className="text-#4b5563 underline cursor-pointer hover:text-#0e355b transition"
onClick={onEmployeeClick}
>
View Details
</button>
view details
</span>
</td>
</tr>
))}
@ -97,8 +95,4 @@ const EmployeeList = ({ className = '' }) => {
);
};
// EmployeeList.propTypes = {
// className: PropTypes.string,
// };
export default EmployeeList;

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

@ -1,124 +1,51 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import ProductRow from "./ProductRow";
import PropTypes from "prop-types";
import Sidebar from '../sidebar/Sidebar';
import '../../styles/ProductList.css';
const CategoryList = ({ className = "" }) => {
const CategoryList = () => {
const navigate = useNavigate();
const handleNavigation = (path) => {
navigate(path);
// Updated categories array with names and specifications
const categories = [
{ name: 'Shirts', image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg', description: 'shirts' },
{ name: 'Suits', image: 'https://img.freepik.com/free-psd/realistic-suit-illustration_23-2151236757.jpg?t=st=1722512180~exp=1722515780~hmac=79030884ba3866ce8461b252477505e74e625c63012705c110eced45b59c63fb&w=740', description: 'suits' },
{ name: 'Tuxedos', image: 'https://img.freepik.com/premium-vector/businessman-suit_98292-5674.jpg?w=740', description: 'tuxedos' },
{ name: 'Jackets', image: 'https://i5.walmartimages.com/seo/Lmtime-Clearance-Men-s-Casual-Blazer-Suit-Jackets-Slim-Fit-Lightweight-Sport-Coat-Business-Coat-Suit-Tops-Jacket-One-Button-Grey-XXL_4277f765-9e49-4ed1-a4b1-c70c71d3d1f0.5c005fe2c8e9d110bfb00216fb52f77b.jpeg?odnHeight=768&odnWidth=768&odnBg=FFFFFF', description: 'jackets' },
];
const handleClick = (category) => {
navigate(`/products/${category}`);
};
return (
<div
className={`w-full bg-primary overflow-y-auto flex flex-row items-start justify-start gap-[3px] leading-[normal] tracking-[normal] text-center text-29xl text-primary1 font-button mq1100:flex-wrap ${className}`}
>
<div className="flex flex-col items-start justify-start py-0 pr-[33px] pl-0">
<div className="overflow-hidden flex flex-col items-start justify-start pt-4 px-5 pb-[50px] gap-[235.5px] mq450:pb-[21px] mq450:box-border mq1025:pt-5 mq1025:pb-8 mq1025:box-border">
<div className="flex flex-col items-start justify-start gap-[146.5px]">
<div className="w-10 h-[147px] relative">
<img
className="absolute h-[27.21%] w-full top-[0%] right-[0%] bottom-[72.79%] left-[0%] max-w-full overflow-hidden max-h-full"
loading="lazy"
alt=""
src="/brooks-logo.svg"
/>
<div className="absolute top-[70px] left-[calc(50%_-_20px)] w-10 h-[77px] hidden">
<div className="absolute top-[0px] left-[0px] w-full h-full hidden">
<div className="absolute top-[0px] left-[0px] rounded-[50px] box-border w-full h-full border-[1px] border-solid border-secondary-accent" />
<div className="absolute top-[40px] left-[3px] rounded-[50%] bg-secondary-accent w-[34px] h-[34px]" />
</div>
</div>
</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-[70px]">
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Home"
src="/home-icon.svg"
onClick={() => handleNavigation('/home')}
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Categories"
src="/category-icon.svg"
onClick={() => handleNavigation('/categories')}
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Customer List"
src="/customer-list-icon.svg"
onClick={() => handleNavigation('/customers')}
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Orders"
src="/orders-icon.svg"
onClick={() => handleNavigation('/employee/create-order')}
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Account"
src="/account-icon.svg"
onClick={() => handleNavigation('/account')}
/>
</div>
</div>
</div>
<div className="flex flex-row items-start justify-start py-0 px-2">
<div className="flex flex-col items-start justify-start">
<img
className="w-6 h-6 relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Logout"
src="/materialsymbolslogout.svg"
onClick={() => handleNavigation('/logout')}
/>
</div>
</div>
<div className="flex h-screen">
{/* Sidebar */}
<Sidebar />
{/* Main content */}
<div className="flex-grow flex flex-col">
<div className="bg-white p-4 flex items-center">
<h1 className="ml-4 text-2xl font-medium text-gray-700">Categories</h1>
</div>
</div>
<div className="flex flex-col items-start justify-start pt-[46px] px-0 pb-0">
<div className="relative leading-[53px] lowercase font-semibold mq450:text-10xl mq450:leading-[32px] mq1025:text-19xl mq1025:leading-[42px]">
categories
</div>
</div>
<div className="w-[820px] flex flex-col items-start justify-start pt-[135px] px-0 pb-0 box-border max-w-full mq750:pt-[57px] mq750:box-border mq1025:pt-[88px] mq1025:box-border">
<div className="self-stretch h-[820px] overflow-y-auto shrink-0 flex flex-row flex-wrap items-start justify-start p-5 box-border relative gap-[80px_76px]">
<ProductRow image17="/image-17@2x.png" shirts="Shirts" />
<ProductRow
image17="/image-18@2x.png"
shirts="Suits"
propLeft="450px"
propTop="20px"
/>
<ProductRow
image17="/image-6@2x.png"
shirts="Tuxedos"
propLeft="20px"
propTop="450px"
/>
<ProductRow
image17="/image-28@2x.png"
shirts="jackets"
propLeft="450px"
propTop="450px"
/>
<div className="flex-grow p-8">
<div className="grid grid-cols-2 gap-8">
{categories.map((category, index) => (
<div
key={index}
className="border border-gray-300 p-4 rounded-lg cursor-pointer hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col items-center"
onClick={() => handleClick(category.name.toLowerCase())} // Updated to handle lowercase category names
>
<img src={category.image} alt={category.name} className="w-48 h-48 object-cover" />
<h2 className="text-xl font-bold text-center mt-2 capitalize">{category.name}</h2>
<p className="ml-4 text-2xl font-medium text-gray-700">{category.description}</p>
<p className="text-center text-sm text-gray-600 mt-1">Click to view products</p>
</div>
))}
</div>
</div>
</div>
</div>
);
};
CategoryList.propTypes = {
className: PropTypes.string,
};
export default CategoryList;

View File

@ -0,0 +1,66 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar';
import ProductRow from './ProductRow';
import '../../styles/ProductList.css';
const products = [
{ id: 1, name: 'red bomber jacket', price: 129, image: 'https://i5.walmartimages.com/seo/Lmtime-Clearance-Men-s-Casual-Blazer-Suit-Jackets-Slim-Fit-Lightweight-Sport-Coat-Business-Coat-Suit-Tops-Jacket-One-Button-Grey-XXL_4277f765-9e49-4ed1-a4b1-c70c71d3d1f0.5c005fe2c8e9d110bfb00216fb52f77b.jpeg?odnHeight=768&odnWidth=768&odnBg=FFFFFF' },
{ id: 2, name: 'green utility jacket', price: 149, image: 'https://i5.walmartimages.com/seo/Lmtime-Clearance-Men-s-Casual-Blazer-Suit-Jackets-Slim-Fit-Lightweight-Sport-Coat-Business-Coat-Suit-Tops-Jacket-One-Button-Grey-XXL_4277f765-9e49-4ed1-a4b1-c70c71d3d1f0.5c005fe2c8e9d110bfb00216fb52f77b.jpeg?odnHeight=768&odnWidth=768&odnBg=FFFFFF' },
{ id: 3, name: 'black leather jacket', price: 199, image: 'https://i5.walmartimages.com/seo/Lmtime-Clearance-Men-s-Casual-Blazer-Suit-Jackets-Slim-Fit-Lightweight-Sport-Coat-Business-Coat-Suit-Tops-Jacket-One-Button-Grey-XXL_4277f765-9e49-4ed1-a4b1-c70c71d3d1f0.5c005fe2c8e9d110bfb00216fb52f77b.jpeg?odnHeight=768&odnWidth=768&odnBg=FFFFFF' },
{ id: 4, name: 'blue aces', price: 99, image: 'https://i5.walmartimages.com/seo/Lmtime-Clearance-Men-s-Casual-Blazer-Suit-Jackets-Slim-Fit-Lightweight-Sport-Coat-Business-Coat-Suit-Tops-Jacket-One-Button-Grey-XXL_4277f765-9e49-4ed1-a4b1-c70c71d3d1f0.5c005fe2c8e9d110bfb00216fb52f77b.jpeg?odnHeight=768&odnWidth=768&odnBg=FFFFFF' },
{ id: 5, name: 'blue birdseye', price: 99, image: 'https://i5.walmartimages.com/seo/Lmtime-Clearance-Men-s-Casual-Blazer-Suit-Jackets-Slim-Fit-Lightweight-Sport-Coat-Business-Coat-Suit-Tops-Jacket-One-Button-Grey-XXL_4277f765-9e49-4ed1-a4b1-c70c71d3d1f0.5c005fe2c8e9d110bfb00216fb52f77b.jpeg?odnHeight=768&odnWidth=768&odnBg=FFFFFF' },
{ id: 6, name: 'blue hydrangea', price: 99, image: 'https://i5.walmartimages.com/seo/Lmtime-Clearance-Men-s-Casual-Blazer-Suit-Jackets-Slim-Fit-Lightweight-Sport-Coat-Business-Coat-Suit-Tops-Jacket-One-Button-Grey-XXL_4277f765-9e49-4ed1-a4b1-c70c71d3d1f0.5c005fe2c8e9d110bfb00216fb52f77b.jpeg?odnHeight=768&odnWidth=768&odnBg=FFFFFF' },
];
const JacketsProductList = () => {
const navigate = useNavigate();
const handleCustomize = (id) => {
navigate(`/customize/${id}`);
};
return (
<div className="flex h-screen">
{/* Sidebar */}
<Sidebar />
{/* Main content */}
<div className="flex-grow p-8 product-list">
<h1 className="ml-4 text-2xl font-medium text-gray-700 ">products (jackets)</h1>
<div className="product-actions">
<p className="text-sm text-gray-600 ">Total jackets: ({products.length})</p>
<div className="flex items-center ">
<svg
className="w-5 h-5 text-gray-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
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>
</div>
</div>
<div className="product-grid">
{products.map((product) => (
<ProductRow
key={product.id}
image17={product.image} // Ensure this matches the prop expected in ProductRow
shirts={product.name} // Ensure this matches the prop expected in ProductRow
onClick={() => handleCustomize(product.id)}
/>
))}
</div>
</div>
</div>
);
};
export default JacketsProductList;

View File

@ -1,7 +1,8 @@
import { useMemo } from "react";
import PropTypes from "prop-types";
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import '../../styles/ProductList.css';
const ProductRow = ({ className = "", image17, shirts, propLeft, propTop }) => {
const ProductRow = ({ className = "", image17, shirts, price, onClick, propLeft, propTop }) => {
const productRowStyle = useMemo(() => {
return {
left: propLeft,
@ -11,37 +12,42 @@ const ProductRow = ({ className = "", image17, shirts, propLeft, propTop }) => {
return (
<div
className={`h-[350px] w-[350px] !m-[0] absolute top-[20px] left-[20px] rounded-3xs box-border flex flex-col items-start justify-start py-5 px-0 gap-[15px] text-center text-29xl text-tertiary font-button border-[0.7px] border-solid border-tertiary ${className}`}
className={`h-88 w-88 m-0 rounded-lg flex flex-col items-start justify-start py-5 px-0 gap-4 text-center text-3xl border border-gray-700 ${className}`}
style={productRowStyle}
>
<img
className="self-stretch flex-1 relative max-w-full overflow-hidden max-h-full object-cover"
className="w-full h-full object-cover"
loading="lazy"
alt=""
alt={shirts}
src={image17}
/>
<div className="self-stretch flex flex-row items-start justify-center py-0 pr-[21px] pl-5">
<div className="relative leading-[53px] lowercase font-semibold inline-block min-w-[124px] mq450:text-10xl mq450:leading-[32px] mq1025:text-19xl mq1025:leading-[42px]">
<div className="w-full flex flex-col items-start py-0 px-5">
<div className="text-xl mb-2 text-gray-700">
{shirts}
</div>
</div>
<div className="flex flex-row items-start justify-start py-0 px-[66px] text-3xl">
<div className="relative leading-[110%] lowercase font-semibold mq450:text-lg mq450:leading-[19px]">
Click to view products
<div className="text-lg mb-4 text-gray-500">
${price}
</div>
</div>
{/* Customize Button */}
<button
className="customize-button w-full py-2 px-4 "
onClick={onClick}
>
customize
</button>
</div>
);
};
ProductRow.propTypes = {
className: PropTypes.string,
image17: PropTypes.string,
shirts: PropTypes.string,
/** Style props */
image17: PropTypes.string.isRequired,
shirts: PropTypes.string.isRequired,
price: PropTypes.string.isRequired, // Ensure price is a required prop
onClick: PropTypes.func.isRequired,
propLeft: PropTypes.any,
propTop: PropTypes.any,
};
export default ProductRow;
export default ProductRow;

View File

@ -0,0 +1,67 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar';
import ProductRow from './ProductRow';
import '../../styles/ProductList.css';
const products = [
{ id: 1, name: 'blue aces', price: 99, image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg' },
{ id: 2, name: 'blue birdseye', price: 99, image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg' },
{ id: 3, name: 'blue hydrangea', price: 99, image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg' },
{ id: 4, name: 'blue aces', price: 99, image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg' },
{ id: 5, name: 'blue birdseye', price: 99, image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg' },
{ id: 6, name: 'blue hydrangea', price: 99, image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg' },
{ id: 7, name: 'blue birdseye', price: 99, image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg' },
{ id: 8, name: 'blue hydrangea', price: 99, image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg' },
];
const ShirtsProductList = () => {
const navigate = useNavigate();
const handleCustomize = (id) => {
navigate(`/customize/${id}`);
};
return (
<div className="flex h-screen">
{/* Sidebar */}
<Sidebar />
{/* Main content */}
<div className="flex-grow p-8 product-list">
<h1 className="ml-4 text-2xl font-medium text-gray-700 " >products (shirts)</h1>
<div className="product-actions">
<p className="text-sm text-gray-600 ">Total shirts: ({products.length})</p>
<div className="flex items-center ">
<svg
className="w-5 h-5 text-gray-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
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>
</div>
</div>
<div className="product-grid">
{products.map((product) => (
<ProductRow
key={product.id}
image17={product.image} // Updated to match prop name
shirts={product.name} // Updated to match prop name
onClick={() => handleCustomize(product.id)}
/>
))}
</div>
</div>
</div>
);
};
export default ShirtsProductList;

View File

@ -0,0 +1,66 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar';
import ProductRow from './ProductRow';
import '../../styles/ProductList.css';
const products = [
{ id: 1, name: 'blue aces', price: 99, image: 'https://img.freepik.com/premium-photo/blue-suit-with-pocket-square-pocket-square_916191-4671.jpg?w=1060' },
{ id: 2, name: 'blue birdseye', price: 99, image: 'https://img.freepik.com/premium-photo/blue-suit-with-pocket-square-pocket-square_916191-4671.jpg?w=1060' },
{ id: 3, name: 'blue hydrangea', price: 99, image: 'https://img.freepik.com/premium-photo/blue-suit-with-pocket-square-pocket-square_916191-4671.jpg?w=1060' },
{ id: 4, name: 'blue aces', price: 99, image: 'https://img.freepik.com/premium-photo/blue-suit-with-pocket-square-pocket-square_916191-4671.jpg?w=1060' },
{ id: 5, name: 'blue birdseye', price: 99, image: 'https://img.freepik.com/premium-photo/blue-suit-with-pocket-square-pocket-square_916191-4671.jpg?w=1060' },
{ id: 6, name: 'blue hydrangea', price: 99, image: 'https://img.freepik.com/premium-photo/blue-suit-with-pocket-square-pocket-square_916191-4671.jpg?w=1060' },
];
const SuitsProductList = () => {
const navigate = useNavigate();
const handleCustomize = (id) => {
navigate(`/customize/${id}`);
};
return (
<div className="flex h-screen">
{/* Sidebar */}
<Sidebar />
{/* Main content */}
<div className="flex-grow p-8 product-list">
<h1 className="ml-4 text-2xl font-medium text-gray-700 ">products (suits)</h1>
<div className="product-actions">
<p className="text-sm text-gray-600 ">Total suits: ({products.length})</p>
<div className="flex items-center ">
<svg
className="w-5 h-5 text-gray-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
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>
</div>
</div>
<div className="product-grid">
{products.map((product) => (
<ProductRow
key={product.id}
image17={product.image}
shirts={product.name}
onClick={() => handleCustomize(product.id)}
/>
))}
</div>
</div>
</div>
);
};
export default SuitsProductList;

View File

@ -0,0 +1,66 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar';
import ProductRow from './ProductRow';
import '../../styles/ProductList.css';
const products = [
{ id: 1, name: 'black tuxedo', price: 199, image: 'https://img.freepik.com/free-photo/mens-suit-photography_1409-5606.jpg?t=st=1722512821~exp=1722516421~hmac=382d80f23936af8160894d570a04e687dc3e41f045803cc88f16eaf0e816583a&w=740' },
{ id: 2, name: 'navy tuxedo', price: 199, image: 'https://img.freepik.com/free-photo/mens-suit-photography_1409-5606.jpg?t=st=1722512821~exp=1722516421~hmac=382d80f23936af8160894d570a04e687dc3e41f045803cc88f16eaf0e816583a&w=740' },
{ id: 3, name: 'grey tuxedo', price: 199, image: 'https://img.freepik.com/free-photo/mens-suit-photography_1409-5606.jpg?t=st=1722512821~exp=1722516421~hmac=382d80f23936af8160894d570a04e687dc3e41f045803cc88f16eaf0e816583a&w=740' },
{ id: 4, name: 'blue aces', price: 99, image: 'https://img.freepik.com/free-photo/mens-suit-photography_1409-5606.jpg?t=st=1722512821~exp=1722516421~hmac=382d80f23936af8160894d570a04e687dc3e41f045803cc88f16eaf0e816583a&w=740' },
{ id: 5, name: 'blue birdseye', price: 99, image: 'https://img.freepik.com/free-photo/mens-suit-photography_1409-5606.jpg?t=st=1722512821~exp=1722516421~hmac=382d80f23936af8160894d570a04e687dc3e41f045803cc88f16eaf0e816583a&w=740' },
{ id: 6, name: 'blue hydrangea', price: 99, image: 'https://img.freepik.com/free-photo/mens-suit-photography_1409-5606.jpg?t=st=1722512821~exp=1722516421~hmac=382d80f23936af8160894d570a04e687dc3e41f045803cc88f16eaf0e816583a&w=740' },
];
const TuxedosProductList = () => {
const navigate = useNavigate();
const handleCustomize = (id) => {
navigate(`/customize/${id}`);
};
return (
<div className="flex h-screen">
{/* Sidebar */}
<Sidebar />
{/* Main content */}
<div className="flex-grow p-8 product-list">
<h1 className="ml-4 text-2xl font-medium text-gray-700 ">products (tuxedos)</h1>
<div className="product-actions">
<p className="text-sm text-gray-600 ">Total tuxedos: ({products.length})</p>
<div className="flex items-center ">
<svg
className="w-5 h-5 text-gray-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
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>
</div>
</div>
<div className="product-grid">
{products.map((product) => (
<ProductRow
key={product.id}
image17={product.image} // Ensure this matches the prop expected in ProductRow
shirts={product.name} // Ensure this matches the prop expected in ProductRow
onClick={() => handleCustomize(product.id)}
/>
))}
</div>
</div>
</div>
);
};
export default TuxedosProductList;

View File

@ -0,0 +1,17 @@
import React from 'react';
import './Button.css'; // Import the CSS file
const Button = ({ onClick, children, style, type = 'button' }) => {
return (
<button
type={type}
onClick={onClick}
className="custom-button"
style={style}
>
{children}
</button>
);
};
export default Button;

View File

@ -0,0 +1,39 @@
import React, { useEffect, useState } from 'react';
import axiosInstance from '../../api/axiosConfig';
const SomeComponent = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await axiosInstance.get('/your-endpoint');
setData(response.data);
setLoading(false);
} catch (err) {
setError(err);
setLoading(false);
}
};
fetchData();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error loading data</p>;
return (
<div>
<h1>Data from API</h1>
<ul>
{data.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
};
export default SomeComponent;

View File

@ -1,148 +1,331 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import React, { useState, useEffect } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig';
import { useNavigate } from 'react-router-dom';
import { createCustomer } from "../../services_1/customersService";
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: ' text-gray-800 font-medium',
formInput: ' p-2 border-b border-gray-400 focus:border-gray-700',
formSelect: ' 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({
firstname: '',
lastname:'',
lastname: '',
age: '',
gender: '',
email: '',
address: '',
mobile: '',
address: {
address_line_1: '',
address_line_2: '',
nearby_landmark: '',
pincode: '',
city_id: '',
},
image: null,
});
const [cities, setCities] = useState([]);
const [imagePreview, setImagePreview] = useState(null);
useEffect(() => {
const fetchCities = async () => {
try {
const response = await axiosInstance.get('/cities');
if (Array.isArray(response.data)) {
setCities(response.data);
} else {
console.error('Error: Data is not an array', response.data);
}
} catch (error) {
console.error('Error fetching cities:', error.response ? error.response.data : error.message);
}
};
fetchCities();
}, []);
// Updated handleInputChange function
const handleInputChange = (e) => {
const { name, value } = e.target;
setCustomerData({
...customerData,
[name]: value,
});
if (name.startsWith('address.')) {
const addressField = name.split('.')[1];
setCustomerData((prevData) => ({
...prevData,
address: {
...prevData.address,
[addressField]: value,
},
}));
} else {
setCustomerData((prevData) => ({
...prevData,
[name]: value,
}));
}
};
const handleImageChange = (e) => {
const file = e.target.files[0];
if (file) {
setCustomerData({
...customerData,
image: file,
});
setImagePreview(URL.createObjectURL(file));
}
};
// Updated handleAddCustomerSubmit function
const handleAddCustomerSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
// Append flat fields
Object.keys(customerData).forEach((key) => {
if (key !== 'address' && key !== 'image') {
formData.append(key, customerData[key]);
}
});
// Append address fields
Object.keys(customerData.address).forEach((key) => {
formData.append(`address[${key}]`, customerData.address[key]);
});
// Append image file
if (customerData.image) {
formData.append('image', customerData.image);
}
try {
const response = await axiosInstance.post('http://localhost:5000/customers', customerData);
const data = await createCustomer(customerData);
console.log('Customer added successfully:', data);
navigate('/customers');
} catch (error) {
console.error(error);
console.error('Error adding customer:', error.response || error.message);
}
};
return (
<div className="flex flex-row">
<div className={CLASSES.container}>
<Sidebar />
<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>
<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={CLASSES.title}>add customer</h1>
</div>
<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-700 w-24 h-24 rounded-full mx-auto"></div>
<button className="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-0e355b text-white text-xs px-2 py-1 rounded-full">
{/* Add Customer Image */}
<div className={CLASSES.formGrid}>
<div className={CLASSES.imageSection}>
<div className={CLASSES.imageWrapper}>
<div className={CLASSES.imageContainer}>
<div className={CLASSES.imagePreview}>
{imagePreview ? (
<img
src={imagePreview}
alt="Customer"
className="w-full h-full object-cover"
/>
) : (
<div className={CLASSES.noImage}>no image</div>
)}
</div>
<input
type="file"
accept="image/*"
className={CLASSES.hiddenInput}
id="imageUpload"
onChange={handleImageChange}
/>
<button
type="button"
onClick={() =>
document.getElementById('imageUpload').click()
}
className={CLASSES.uploadButton}
>
</button>
</div>
</div>
<div>
<Link to="/">
<h2 className="text-lg font-semibold text-">customer information</h2>
<Link to="/add-customer">
<h2 className={CLASSES.linkTitle}>customer information</h2>
</Link>
<Link to="/measurements">
<h2 className="text-lg font-semibold text-">measurements</h2>
<h2 className={CLASSES.linkTitle}>measurements</h2>
</Link>
</div>
</div>
<div className="md:col-span-2 space-y-4">
<form onSubmit={handleAddCustomerSubmit}>
<div>
<label className="block text-d1d5db font-medium text-d1d5db">customer firstname:</label>
<div className={CLASSES.formSection}>
<form onSubmit={handleAddCustomerSubmit} className="space-y-4">
<div className={CLASSES.formGroup}>
<label htmlFor="firstname">First Name</label>
<input
type="text"
id="firstname"
name="firstname"
className={CLASSES.formInput}
value={customerData.firstname}
onChange={handleInputChange}
className="input-underline"
/>
</div>
<div>
<label className="block text-d1d5db font-medium text-d1d5db">customer lastname:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="lastname">Last Name</label>
<input
type="text"
id="lastname"
name="lastname"
className={CLASSES.formInput}
value={customerData.lastname}
onChange={handleInputChange}
className="input-underline"
/>
</div>
<div>
<label className="block text-d1d5db font-medium text-d1d5db">age:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="age">Age</label>
<input
type="number"
id="age"
name="age"
className={CLASSES.formInput}
value={customerData.age}
onChange={handleInputChange}
className="input-underline"
/>
</div>
<div>
<label className="block text-d1d5db font-medium text-d1d5db">gender:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="gender">Gender</label>
<select
id="gender"
name="gender"
className={CLASSES.formSelect}
value={customerData.gender}
onChange={handleInputChange}
className="input-underline"
>
<option value="" disabled>select gender</option>
<option value="male">male</option>
<option value="female">female</option>
<option value="other">other</option>
<option value="">Select gender</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
</select>
</div>
<div>
<label className="block text-d1d5db font-medium text-d1d5db">email:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
className={CLASSES.formInput}
value={customerData.email}
onChange={handleInputChange}
className="input-underline"
/>
</div>
<div>
<label className="block text-d1d5db font-medium text-d1d5db">address:</label>
<input
type="text"
name="address"
value={customerData.address}
onChange={handleInputChange}
className="input-underline"
/>
</div>
<div>
<label className="block text-d1d5db font-medium text-d1d5db">mobile no:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="mobile">Mobile Number</label>
<input
type="text"
id="mobile"
name="mobile"
className={CLASSES.formInput}
value={customerData.mobile}
onChange={handleInputChange}
className="input-underline"
/>
</div>
<br />
<div className="text-center">
<button type="submit" className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md font-semibold text-sm">
save information
<div className={CLASSES.formGroup}>
<label htmlFor="address_line_1">Address Line 1</label>
<input
type="text"
id="address_line_1"
name="address.address_line_1"
className={CLASSES.formInput}
value={customerData.address.address_line_1}
onChange={handleInputChange}
/>
</div>
<div className={CLASSES.formGroup}>
<label htmlFor="address_line_2">Address Line 2</label>
<input
type="text"
id="address_line_2"
name="address.address_line_2"
className={CLASSES.formInput}
value={customerData.address.address_line_2}
onChange={handleInputChange}
/>
</div>
<div className={CLASSES.formGroup}>
<label htmlFor="nearby_landmark">Nearby Landmark</label>
<input
type="text"
id="nearby_landmark"
name="address.nearby_landmark"
className={CLASSES.formInput}
value={customerData.address.nearby_landmark}
onChange={handleInputChange}
/>
</div>
<div className={CLASSES.formGroup}>
<label htmlFor="pincode">Pincode</label>
<input
type="text"
id="pincode"
name="address.pincode"
className={CLASSES.formInput}
value={customerData.address.pincode}
onChange={handleInputChange}
/>
</div>
<div className={CLASSES.formGroup}>
<label htmlFor="city_id">City</label>
<select
id="city_id"
name="address.city_id"
className={CLASSES.formSelect}
value={customerData.address.city_id}
onChange={handleInputChange}
>
<option value="">Select City</option>
{cities.map((city) => (
<option key={city.id} value={city.id}>
{city.name}
</option>
))}
</select>
</div>
<div className={CLASSES.formGroup}>
<button type="submit" className={CLASSES.submitButton}>
save customer
</button>
</div>
</form>

View File

@ -2,21 +2,23 @@ import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig';
import '../../styles/CustomerList.css';
import {getAllCustomers} from '../../services_1/customersService'
const CustomerList = () => {
const [customers, setCustomers] = useState([]);
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
setCustomers(customers_data);
const data = await getAllCustomers()
setCustomers(data);
} catch (error) {
throw error;
}
@ -31,18 +33,18 @@ 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">
<h1 className="text-2xl font-semibold text-gray-800">Customers List</h1>
<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"
className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md text-sm"
onClick={handleAddCustomer}
>
Add New Customer
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"
@ -55,9 +57,9 @@ const CustomerList = () => {
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 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">

View File

@ -3,13 +3,14 @@ import { Button, Input, Form } from 'antd';
import { Link } from 'react-router-dom';
import axios from 'axios';
import Sidebar from '../sidebar/Sidebar';
import '../../styles/CustomerMeasurements.css';
const CustomerMeasurements = () => {
const onFinish = async (values) => {
const token = localStorage.getItem('token'); // Assuming the token is stored in localStorage
try {
const response = await axios.post('http://localhost:8080/measurements', values, {
const response = await axios.post('http://localhost:5000/measurements', values, {
headers: {
Authorization: `Bearer ${token}`,
},
@ -22,55 +23,57 @@ const CustomerMeasurements = () => {
};
return (
<div className="flex flex-row">
<div className="container flex">
<Sidebar />
<div className="w-full flex flex-col min-h-screen text-white">
<div className="min-h-screen text-#4b5563 p-6">
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-semibold text-#0e355b">Customer Measurements</h1>
<Link to="/CategoryList" className="text-#0e355b">skip for now</Link>
<div className="content flex-1">
<div className="form-container max-w-2xl mx-auto p-6">
<div className="header flex justify-between items-left mb-4">
<h1 className="mml-4 text-2xl font-semibold text-gray-700">customer measurements</h1>
<Link to="/CategoryList" className="skip-link ">skip for now</Link>
</div>
<Form onFinish={onFinish} className="space-y-4">
<h2 className="text-lg font-semibold text-#0e355b">Upper Body Measurements</h2>
<Form.Item name="neck" label={<span className="text-#0e355b">Neck Circumference (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form onFinish={onFinish} className="form space-y-4">
<h2 className="section-title text-lg font-medium">Upper Body Measurements</h2>
<Form.Item name="neck" label="Neck Circumference (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="chest" label={<span className="text-#0e355b">Chest Circumference (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form.Item name="chest" label="Chest Circumference (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="waist" label={<span className="text-#0e355b">Waist Circumference (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form.Item name="waist" label="Waist Circumference (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="shoulder" label={<span className="text-#0e355b">Shoulder Width (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form.Item name="shoulder" label="Shoulder Width (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="arm" label={<span className="text-#0e355b">Arm Length (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form.Item name="arm" label="Arm Length (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="sleeve" label={<span className="text-#0e355b">Sleeve Length (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form.Item name="sleeve" label="Sleeve Length (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<h2 className="text-lg font-semibold text-#0e355b">Lower Body Measurements</h2>
<Form.Item name="hip" label={<span className="text-#0e355b">Hip Circumference (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<h2 className="section-title text-lg font-medium">Lower Body Measurements</h2>
<Form.Item name="hip" label="Hip Circumference (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="inseam" label={<span className="text-#0e355b">Inseam (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form.Item name="inseam" label="Inseam (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="outseam" label={<span className="text-#0e355b">Out Seam (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form.Item name="outseam" label="Outseam (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="thigh" label={<span className="text-#0e355b">Thigh Circumference (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form.Item name="thigh" label="Thigh Circumference (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="ankle" label={<span className="text-#0e355b">Ankle Circumference (in inch)</span>}>
<Input className="block text-sm font-medium text-#4b5563" />
<Form.Item name="ankle" label="Ankle Circumference (in inch)">
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</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="button-container flex justify-end">
<Button type="primary" htmlType="submit" className="submit-button bg-[#0e355b] text-white py-2 px-4 rounded-md">
Save Measurements
</Button>
</div>
</Form.Item>
</Form>
</div>

View File

@ -0,0 +1,188 @@
import React from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import Sidebar from '../sidebar/Sidebar';
import '../../styles/Measurements.css';
const CustomMeasurements = () => {
const navigate = useNavigate();
const handleCheckout = () => {
navigate('/checkout');
};
return (
<div className="flex">
<Sidebar />
<div className="w-full ml-10 p-2">
<div className="bg-white p-4 flex items-center">
<Link to="/setmeasurements">
<FontAwesomeIcon icon={faChevronLeft} />
</Link>
<h1 className="ml-4 text-2xl font-medium text-gray-700">custom measurements</h1>
</div>
<div className="mt-4 flex-">
<img
src="https://5.imimg.com/data5/HM/AE/VV/SELLER-77211399/white-plain-tshirts.jpg"
alt="Product"
className="w-1/3 h-auto mr-4"
/>
<div className="w-2/3">
<p className="text-xl mb-2 text-gray-700">product name: blue aces</p>
<p className="text-xl mb-4 text-gray-700">$99.00</p>
<form className="bg-white p-6 rounded shadow">
<h2 className="text-lg mb-4 text-gray-700 font-semibold">upper body measurements</h2>
<div className="flex flex-wrap mb-4">
<div className="w-1/2 mb-2">
<label htmlFor="neck-circumference" className="block text-sm font-medium text-gray-700">
neck circumference (in inch):
</label>
<input
id="neck-circumference"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter neck size"
/>
</div>
<div className="w-1/2 mb-2">
<label htmlFor="chest-circumference" className="block text-sm font-medium text-gray-700">
chest circumference (in inch):
</label>
<input
id="chest-circumference"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter chest size"
/>
</div>
<div className="w-1/2 mb-2">
<label htmlFor="waist-circumference" className="block text-sm font-medium text-gray-700">
waist circumference (in inch):
</label>
<input
id="waist-circumference"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter waist size"
/>
</div>
<div className="w-1/2 mb-2">
<label htmlFor="shoulder-width" className="block text-sm font-medium text-gray-700">
shoulder width (in inch):
</label>
<input
id="shoulder-width"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter shoulder size"
/>
</div>
<div className="w-1/2 mb-2">
<label htmlFor="arm-length" className="block text-sm font-medium text-gray-700">
arm length (in inch):
</label>
<input
id="arm-length"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter arm size"
/>
</div>
<div className="w-1/2 mb-2">
<label htmlFor="sleeve-length" className="block text-sm font-medium text-gray-700">
sleeve length (in inch):
</label>
<input
id="sleeve-length"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter sleeve size"
/>
</div>
</div>
<h2 className="text-lg mb-4 text-gray-700 font-semibold">lower body measurements</h2>
<div className="flex flex-wrap mb-4">
<div className="w-1/2 mb-2">
<label htmlFor="hip-circumference" className="block text-sm font-medium text-gray-700">
hip circumference (in inch):
</label>
<input
id="hip-circumference"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter hip size"
/>
</div>
<div className="w-1/2 mb-2">
<label htmlFor="inseam" className="block text-sm font-medium text-gray-700">
inseam (in inch):
</label>
<input
id="inseam"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter inseam size"
/>
</div>
<div className="w-1/2 mb-2">
<label htmlFor="outseam" className="block text-sm font-medium text-gray-700">
outseam (in inch):
</label>
<input
id="outseam"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter outseam size"
/>
</div>
<div className="w-1/2 mb-2">
<label htmlFor="thigh-circumference" className="block text-sm font-medium text-gray-700">
thigh circumference (in inch):
</label>
<input
id="thigh-circumference"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter thigh size"
/>
</div>
<div className="w-1/2 mb-2">
<label htmlFor="ankle-circumference" className="block text-sm font-medium text-gray-700">
ankle circumference (in inch):
</label>
<input
id="ankle-circumference"
type="text"
className="mt-1 block border border-gray-300 rounded py-2 px-3"
placeholder="Enter ankle size"
/>
</div>
</div>
<button
type="submit"
className="py-2 px-4 mb-4 "
>
save measurements
</button>
<span className="text-gray-700 cursor-pointer hover:underline ml-2">
edit measurements
</span>
</form>
<button
onClick={handleCheckout}
className="mt-6 py-2 px-4 "
>
continue to checkout
</button>
</div>
</div>
</div>
</div>
);
};
export default CustomMeasurements;

View File

@ -0,0 +1,142 @@
import React, { useState, useEffect } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import Sidebar from '../sidebar/Sidebar';
import '../../styles/Customize.css';
const Customize = () => {
const { id } = useParams();
const navigate = useNavigate();
const [style, setStyle] = useState('');
const [material, setMaterial] = useState('');
const [productImage, setProductImage] = useState('');
useEffect(() => {
// Fetch the product image URL based on the product ID
const fetchProductImage = async () => {
try {
const response = await fetch(`/api/products/${id}`);
const data = await response.json();
setProductImage(data.imageUrl); // Assume `data.imageUrl` is the URL of the product image
} catch (error) {
console.error('Error fetching product image:', error);
}
};
fetchProductImage();
}, [id]);
const handleStyleChange = (event) => {
setStyle(event.target.value);
};
const handleMaterialChange = (event) => {
setMaterial(event.target.value);
};
const handleSave = () => {
const customization = {
productId: id,
style,
material,
};
navigate('/setmeasurements');
fetch('/api/saveCustomization', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(customization),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
navigate('/setmeasurements'); // Redirect to the Set Measurements page
})
.catch((error) => {
console.error('Error:', error);
});
};
return (
<div className="flex h-screen">
{/* Sidebar */}
<Sidebar />
{/* Main content */}
<div className="flex-grow flex flex-col">
{/* Header */}
<div className="bg-white p-4 flex items-center ">
<Link to="/CategoryList">
<FontAwesomeIcon icon={faChevronLeft} className="text-gray-700" />
</Link>
<h1 className="ml-4 text-2xl font-medium text-gray-700">Customize Product</h1>
</div>
{/* Content */}
<div className="flex-grow flex items-stretch p-8">
{/* Customization Options */}
<div className="bg-white p-6 rounded-lg w-1/3 mr-4">
<h2 className="text-lg font-medium text-gray-700 mb-4">Customize</h2>
{/* Style Selection */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700">
Style:
<select
value={style}
onChange={handleStyleChange}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
>
<option value="">Select style</option>
<option value="collar">Collar</option>
<option value="cuff">Cuff</option>
<option value="placket">Placket</option>
</select>
</label>
</div>
{/* Material Selection */}
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700">
Material:
<select
value={material}
onChange={handleMaterialChange}
className="mt-1 block w-full py-2 px-3 border border-gray-300 rounded-md"
>
<option value="">Select material</option>
<option value="cotton">Cotton</option>
<option value="polyester">Polyester</option>
<option value="linen">Linen</option>
</select>
</label>
</div>
{/* Save Button */}
<button
onClick={handleSave}
className="w-full py-2 px-4"
>
Save Style and Material
</button>
</div>
{/* Product Image */}
<div className="bg-white p-6 rounded-lg flex-grow">
{productImage ? (
<img src={productImage} alt="Product" className="w-full h-auto rounded-lg" />
) : (
<p className="ml-4 text-xl text-gray-700" >Loading image...</p>
)}
</div>
</div>
</div>
</div>
);
};
export default Customize;

View File

@ -0,0 +1,70 @@
import React, { useState } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import Sidebar from '../sidebar/Sidebar';
const SetMeasurements = () => {
const [selectedOption, setSelectedOption] = useState('standard');
const navigate = useNavigate();
const handleOptionChange = (event) => {
setSelectedOption(event.target.value);
};
const handleContinue = () => {
if (selectedOption === 'standard') {
navigate('/measurements/standard');
} else {
navigate('/measurements/custom');
}
};
return (
<div className="flex h-screen">
<Sidebar />
<div className="flex-grow flex flex-col">
<div className="bg-white p-4 flex items-center">
<Link to="/customize/:id">
<FontAwesomeIcon icon={faChevronLeft} />
</Link>
<h1 className="ml-4 text-2xl font-medium text-gray-700">set measurements</h1>
</div>
<div className="flex-grow flex items-start justify-end p-8">
<div className="bg-white p-6 rounded-lg w-full max-w-md">
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700">
<input
type="radio"
value="standard"
checked={selectedOption === 'standard'}
onChange={handleOptionChange}
className="mr-2"
/>
standard measurements
</label>
<label className="block text-sm font-medium text-gray-700 mt-2">
<input
type="radio"
value="custom"
checked={selectedOption === 'custom'}
onChange={handleOptionChange}
className="mr-2"
/>
custom measurements
</label>
</div>
<button
onClick={handleContinue}
className=" py-2 px-4 "
>
continue
</button>
</div>
</div>
</div>
</div>
);
};
export default SetMeasurements;

View File

@ -0,0 +1,66 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
import Sidebar from '../sidebar/Sidebar';
import '../../styles/Measurements.css';
const StandardMeasurements = () => {
const [selectedSize, setSelectedSize] = useState('medium');
const handleSizeChange = (event) => {
setSelectedSize(event.target.value);
};
return (
<div className="flex">
<Sidebar />
<div className="w-full ml-10 p-2">
<div className="bg-white p-4 flex items-center">
<Link to="/setmeasurements">
<FontAwesomeIcon icon={faChevronLeft} />
</Link>
<h1 className="ml-4 text-2xl font-medium text-gray-700">standard measurements</h1>
</div>
<div className="flex mt-4">
<img
src="https://5.imimg.com/data5/HM/AE/VV/SELLER-77211399/white-plain-tshirts.jpg"
alt="Product"
className="w-1/3 h-auto mr-4"
/>
<div className="w-2/3">
<p className="text-xl mb-4 text-gray-700">blue aces</p>
<p className="text-md mb-4 text-gray-600">
this is a comfortable and stylish blue t-shirt made from high-quality cotton. perfect for casual outings and daily wear.
</p>
<p className="text-xl mb-4 text-gray-700">$99.00</p>
<div className="mb-4 select-container">
<label htmlFor="size" className="block text-sm font-medium text-gray-700">
size:
</label>
<select
id="size"
value={selectedSize}
onChange={handleSizeChange}
className="custom-select mt-1 block "
>
<option value="s">s</option>
<option value="m">m</option>
<option value="l">l</option>
<option value="xl">xl</option>
<option value="xxl">xxl</option>
</select>
</div>
<p className="text-xl mb-4 text-gray-700">selected size: {selectedSize}</p> {/* Display selected size */}
<button className="mt-4 py-2 px-4">
continue to checkout
</button>
</div>
</div>
</div>
</div>
);
};
export default StandardMeasurements;

View File

@ -1,19 +0,0 @@
.dashboard {
padding: 20px;
background-color: #ffffff;
}
.dashboard-title {
font-size: 24px;
margin-bottom: 20px;
}
.dashboard-content {
display: flex;
flex-direction: column;
align-items: center;
}
.dashboard-content .ant-btn {
margin: 10px 0;
}

View File

@ -1,49 +0,0 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar';
const HomePage = () => {
const navigate = useNavigate();
const handleClick = (link) => {
navigate(link); // Redirects to the '/customers' route
};
return (
<div className="flex flex-row">
<Sidebar />
<div className="w-full flex flex-col min-h-screen text-white">
<header className="w-full p-4 flex justify-between items-center">
<div className="flex items-center">
<h1 className="text-#0e355b text-2xl font-bold">home</h1>
</div>
<div>
<p className="text-#4b5563">welcome back (ester howard)</p>
</div>
</header>
<main className="flex-1 flex flex-col items-center justify-center p-6">
<div className="w-full max-w-3xl text-center">
<img src="path_to_image" alt="Suit" className="w-full rounded-md mb-6" />
<button
className="w-full py-4 bg-#0e355b text-#d1d5db rounded-md font-semibold mb-4 hover:bg-#154676"
onClick={() => handleClick('/customers')}
>
create a new order
</button>
<button
className="w-full py-4 bg-#0e355b text-#d1d5db rounded-md font-semibold mb-4 hover:bg-#154676"
onClick={() => handleClick('')}
>
view orders
</button>
<button
className="w-full py-4 bg-#0e355b text-#d1d5db rounded-md font-semibold mb-4 hover:bg-#154676"
onClick={() => handleClick('')}
>
view customers
</button>
</div>
</main>
</div>
</div>
);
};
export default HomePage;

View File

View File

@ -1,7 +0,0 @@
import React from 'react';
const ViewOrders = () => {
return <div>View Orders Page</div>;
};
export default ViewOrders;

View File

@ -1,10 +1,8 @@
// src/components/sidebar/AdminSidebar.js
import React, { useState } from 'react';
import { Layout, Drawer, Button } from 'antd';
import { Layout } from 'antd';
import { Link } from 'react-router-dom';
import {
MenuFoldOutlined,
MenuUnfoldOutlined,
} from '@ant-design/icons';
import BrookslogoIcon from '../../assets/thob-data/BrookslogoIcon.svg';
import HomeIcon from '../../assets/thob-data/HomeIcon.svg';
import AddCustomerIcon from '../../assets/thob-data/AddCustomerIcon.svg';
@ -15,61 +13,62 @@ import EmployeeIcon from '../../assets/thob-data/EmployeeIcon.svg';
import MeasurmentsIcon from '../../assets/thob-data/MeasurmentsIcon.svg';
import ProfileIcon from '../../assets/thob-data/ProfileIcon.svg';
import LogoutIcon from '../../assets/thob-data/LogoutIcon.svg';
import '../../styles/AdminSidebar.css'; // Import the CSS file
const { Sider } = Layout;
const classNames = {
sider: 'desktop-sidebar',
logoContainer: 'flex items-center justify-center h-16 w-16 m-5 bg-white',
iconContainer: 'flex flex-col items-center',
iconLink: 'my-6',
icon: 'hover:opacity-75 sidebar-icon',
};
const AdminSidebar = () => {
const [visible, setVisible] = useState(false);
const showDrawer = () => {
setVisible(true);
};
const closeDrawer = () => {
setVisible(false);
};
return (
<>
<Sider
className="desktop-sidebar"
className={classNames.sider}
breakpoint="lg"
collapsedWidth="0"
width={80}
style={{ height: '100vh' }}
>
<div className="flex items-center justify-center h-16 w-16 m-5 bg-white" style={{ width: '40px', height: '40px', marginTop: '16px', marginLeft: '20px' }}>
<Link to="/admin" className="my-6">
<img src={BrookslogoIcon} alt="Home" className="hover:opacity-75 sidebar-icon" />
<div className={classNames.logoContainer} style={{ width: '40px', height: '40px', marginTop: '16px', marginLeft: '20px' }}>
<Link to="/admin" className={classNames.iconLink}>
<img src={BrookslogoIcon} alt="Home" className={classNames.icon} />
</Link>
</div>
<div className="flex flex-col items-center">
<Link to="/admin/home" className="my-6">
<img src={HomeIcon} alt="Home" className="hover:opacity-75 sidebar-icon" />
<div className={classNames.iconContainer}>
<Link to="/admin" className={classNames.iconLink}>
<img src={HomeIcon} alt="Home" className={classNames.icon} />
</Link>
<Link to="/admin/categories" className="my-6">
<img src={CategoriesIcon} alt="Categories" className="hover:opacity-75 sidebar-icon" />
<Link to="/CategoryList" className={classNames.iconLink}>
<img src={CategoriesIcon} alt="Categories" className={classNames.icon} />
</Link>
<Link to="/admin/add-customer" className="my-6">
<img src={AddCustomerIcon} alt="Add Customer" className="hover:opacity-75 sidebar-icon" />
<Link to="/admin/add-customer" className={classNames.iconLink}>
<img src={AddCustomerIcon} alt="Add Customer" className={classNames.icon} />
</Link>
<Link to="/admin/orders" className="my-6">
<img src={OrdersIcon} alt="Orders" className="hover:opacity-75 sidebar-icon" />
<Link to="/admin/orders" className={classNames.iconLink}>
<img src={OrdersIcon} alt="Orders" className={classNames.icon} />
</Link>
<Link to="/admin/catalog" className="my-6">
<img src={CatalogIcon} alt="Catalog" className="hover:opacity-75 sidebar-icon" />
<Link to="/admin/catalog" className={classNames.iconLink}>
<img src={CatalogIcon} alt="Catalog" className={classNames.icon} />
</Link>
<Link to="/admin/employee" className="my-6">
<img src={EmployeeIcon} alt="Employee" className="hover:opacity-75 sidebar-icon" />
<Link to="/employees" className={classNames.iconLink}>
<img src={EmployeeIcon} alt="Employee" className={classNames.icon} />
</Link>
<Link to="/admin/measurements" className="my-6">
<img src={MeasurmentsIcon} alt="Measurements" className="hover:opacity-75 sidebar-icon" />
<Link to="/setmeasurements" className={classNames.iconLink}>
<img src={MeasurmentsIcon} alt="Measurements" className={classNames.icon} />
</Link>
<Link to="/admin/profile" className="my-6">
<img src={ProfileIcon} alt="Profile" className="hover:opacity-75 sidebar-icon" />
<Link to="/admin/profile" className={classNames.iconLink}>
<img src={ProfileIcon} alt="Profile" className={classNames.icon} />
</Link>
<Link to="/admin/logout" className="my-6">
<img src={LogoutIcon} alt="Logout" className="hover:opacity-75 sidebar-icon" />
<Link to="/admin/logout" className={classNames.iconLink}>
<img src={LogoutIcon} alt="Logout" className={classNames.icon} />
</Link>
</div>
</Sider>

View File

@ -1,10 +1,6 @@
import React, { useState } from 'react';
import { Layout, Drawer, Button } from 'antd';
import React from 'react';
import { Layout } from 'antd';
import { Link } from 'react-router-dom';
import {
MenuFoldOutlined,
MenuUnfoldOutlined,
} from '@ant-design/icons';
import BrookslogoIcon from '../../assets/thob-data/BrookslogoIcon.svg';
import HomeIcon from '../../assets/thob-data/HomeIcon.svg';
import AddCustomerIcon from '../../assets/thob-data/AddCustomerIcon.svg';
@ -12,62 +8,60 @@ import OrdersIcon from '../../assets/thob-data/OrdersIcon.svg';
import CategoriesIcon from '../../assets/thob-data/CategoriesIcon.svg';
import ProfileIcon from '../../assets/thob-data/ProfileIcon.svg';
import LogoutIcon from '../../assets/thob-data/LogoutIcon.svg';
import '../../styles/EmployeeSidebar.css';
const { Sider } = Layout;
const classNames = {
sider: 'desktop-sidebar',
container: 'flex flex-col items-center mt-4 h-full justify-between',
logoContainer: 'flex items-center justify-center h-16 w-16 mb-5 bg-white',
iconContainer: 'flex flex-col items-center flex-grow',
iconLink: 'my-6',
icon: 'hover:opacity-75 sidebar-icon',
logoutContainer: 'flex items-center justify-center mb-4',
};
const EmployeeSidebar = () => {
const [visible, setVisible] = useState(false);
const showDrawer = () => {
setVisible(true);
};
const closeDrawer = () => {
setVisible(false);
};
return (
<>
<Sider
className="desktop-sidebar"
breakpoint="lg"
collapsedWidth="0"
width={70}
style={{ height: '100vh' }}
>
<div className="flex items-center justify-center h-16 w-16 m-5 bg-white" style={{ width: '40px', height: '40px', marginTop: '16px', marginLeft: '20px' }}>
<Link to="/employee" className="my-6">
<img src={BrookslogoIcon} alt="Home" className="hover:opacity-75 sidebar-icon" />
<Sider
className={classNames.sider}
breakpoint="lg"
collapsedWidth="0"
width={70}
style={{ height: '100vh', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}
>
<div className={classNames.container}>
<div className={classNames.logoContainer}>
<Link to="">
<img src={BrookslogoIcon} alt="Brooks Bingham Logo" className={classNames.icon} />
</Link>
</div>
<div className="flex flex-col items-center">
<Link to="/employee/home" className="my-6">
<img src={HomeIcon} alt="Home" className="hover:opacity-75 sidebar-icon" />
<div className={classNames.iconContainer}>
<Link to="/employee/home" className={classNames.iconLink}>
<img src={HomeIcon} alt="Home" className={classNames.icon} />
</Link>
<Link to="/employee/categories" className="my-6">
<img src={CategoriesIcon} alt="Categories" className="hover:opacity-75 sidebar-icon" />
<Link to="/CategoryList" className={classNames.iconLink}>
<img src={CategoriesIcon} alt="Categories" className={classNames.icon} />
</Link>
<Link to="/customers" className="my-6">
<img src={AddCustomerIcon} alt="Add Customer" className="hover:opacity-75 sidebar-icon" />
<Link to="/customers" className={classNames.iconLink}>
<img src={AddCustomerIcon} alt="Add Customer" className={classNames.icon} />
</Link>
<Link to="/employee/orders" className="my-6">
<img src={OrdersIcon} alt="Orders" className="hover:opacity-75 sidebar-icon" />
<Link to="/order" className={classNames.iconLink}>
<img src={OrdersIcon} alt="Orders" className={classNames.icon} />
</Link>
<Link to="/employee/profile" className="my-6">
<img src={ProfileIcon} alt="Profile" className="hover:opacity-75 sidebar-icon" />
</Link>
<Link to="/employee/logout" className="my-6">
<img src={LogoutIcon} alt="Logout" className="hover:opacity-75 sidebar-icon" />
<Link to="/profile" className={classNames.iconLink}>
<img src={ProfileIcon} alt="Profile" className={classNames.icon} />
</Link>
</div>
</Sider>
</>
</div>
<div className={classNames.logoutContainer}>
<Link to="/employee/logout">
<img src={LogoutIcon} alt="Logout" className={classNames.icon} />
</Link>
</div>
</Sider>
);
};
export default EmployeeSidebar;

View File

@ -56,4 +56,7 @@
.menu-item {
margin: 0 10px;
}
}

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

@ -0,0 +1,61 @@
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../components/sidebar/Sidebar';
import '../styles/AdminDashboard.css';
const AdminDashboard = () => {
const navigate = useNavigate();
const [userName, setUserName] = useState('');
useEffect(() => {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.name) {
setUserName(user.name);
}
}, []);
const handleClick = (link) => {
navigate(link);
};
return (
<div className="dashboard-container">
<Sidebar />
<div className="dashboard-content">
<header className="dashboard-header">
<h1 className="header-title">admin dashboard</h1>
<p className="welcome-text">welcome back, {userName}</p>
</header>
<main className="dashboard-main">
<div className="dashboard-main-content">
<img
src="https://img.freepik.com/free-photo/well-dressed-businessman-holding-currency-with-confidence-generated-by-ai_188544-16970.jpg?t=st=1722511417~exp=1722515017~hmac=dbe5ab19f8e2c416be4928faac1fc197473166fc4f230d8f46307b84e522ff5a&w=1060"
alt="Dashboard Graphic"
className="dashboard-image"
/>
<button
className="btn-primary"
onClick={() => handleClick('')}
>
view orders
</button>
<button
className="btn-primary"
onClick={() => handleClick('/employees')}
>
view employees
</button>
<button
className="btn-primary"
onClick={() => handleClick('')}
>
view customers
</button>
</div>
</main>
</div>
</div>
);
};
export default AdminDashboard;

View File

@ -0,0 +1,73 @@
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Sidebar from '../components/sidebar/Sidebar';
// import '../../styles/EmployeeDashboard.css';
const classNames = {
container: 'dashboard-container',
content: 'dashboard-content',
header: 'dashboard-header',
headerTitle: 'header-title',
welcomeText: 'welcome-text',
main: 'dashboard-main',
mainContent: 'dashboard-main-content',
image: 'dashboard-image',
button: 'btn-primary',
};
const EmployeeDashboard = () => {
const navigate = useNavigate();
const [userName, setUserName] = useState('');
useEffect(() => {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.name) {
setUserName(user.name);
}
}, []);
const handleClick = (link) => {
navigate(link);
};
return (
<div className={classNames.container}>
<Sidebar />
<div className={classNames.content}>
<header className={classNames.header}>
<div className="flex items-center">
<h1 className={classNames.headerTitle}>employee dashboard</h1>
</div>
<div>
<p className={classNames.welcomeText}>welcome back, {userName}</p>
</div>
</header>
<main className={classNames.main}>
<div className={classNames.mainContent}>
<img src="https://img.freepik.com/free-photo/well-dressed-businessman-holding-currency-with-confidence-generated-by-ai_188544-16970.jpg?t=st=1722511417~exp=1722515017~hmac=dbe5ab19f8e2c416be4928faac1fc197473166fc4f230d8f46307b84e522ff5a&w=1060" alt="Suit" className={classNames.image} />
<button
className={classNames.button}
onClick={() => handleClick('/customers')}
>
create a new order
</button>
<button
className={classNames.button}
onClick={() => handleClick('')}
>
view orders
</button>
<button
className={classNames.button}
onClick={() => handleClick('')}
>
view customers
</button>
</div>
</main>
</div>
</div>
);
};
export default EmployeeDashboard;

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;

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

@ -0,0 +1,75 @@
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 = [
{ email: 'admin@example.com', password: 'admin123', role: 'admin' },
{ email: 'employee@example.com', password: 'employee123', role: 'employee' },
];
// Endpoint to handle login requests
app.post('/auth/login', (req, res) => {
const { email, password } = req.body;
const user = users.find(user => user.email === email && user.password === password);
if (!user) {
return res.status(401).json({ success: false, message: 'Invalid email or password' });
}
res.status(200).json({
success: true,
message: 'Login successful',
role: user.role,
});
});
// 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}`);
});

75
src/services_1/Orders.jsx Normal file
View File

@ -0,0 +1,75 @@
import React, { useState, useEffect } from 'react';
import ordersService from './ordersService';
const Orders = () => {
const [orders, setOrders] = useState([]);
const [order, setOrder] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
fetchOrders();
}, []);
const fetchOrders = async () => {
try {
const data = await ordersService.getAllOrders();
setOrders(data);
} catch (error) {
setError('Error fetching orders');
}
};
const fetchOrderById = async (id) => {
try {
const data = await ordersService.getOrderById(id);
setOrder(data);
} catch (error) {
setError('Error fetching order');
}
};
const addOrder = async (orderData) => {
try {
const data = await ordersService.createOrder(orderData);
setOrders([...orders, data]);
} catch (error) {
setError('Error adding order');
}
};
const updateOrder = async (id, updatedData) => {
try {
const data = await ordersService.updateOrder(id, updatedData);
setOrders(orders.map(o => (o.id === id ? data : o)));
} catch (error) {
setError('Error updating order');
}
};
const deleteOrder = async (id) => {
try {
await ordersService.deleteOrder(id);
setOrders(orders.filter(o => o.id !== id));
} catch (error) {
setError('Error deleting order');
}
};
return (
<div>
<h1>Orders</h1>
{error && <p>{error}</p>}
{/* Render orders list here */}
{/* Example of calling fetchOrderById */}
{/* <button onClick={() => fetchOrderById('order-id')}>Fetch Order</button> */}
{/* Example of calling addOrder */}
{/* <button onClick={() => addOrder({ subtotal: 100, tax_rate: 0.1, tax_amount: 10, total: 110, status: 'pending' })}>Add Order</button> */}
{/* Example of calling updateOrder */}
{/* <button onClick={() => updateOrder('order-id', { status: 'shipped' })}>Update Order</button> */}
{/* Example of calling deleteOrder */}
{/* <button onClick={() => deleteOrder('order-id')}>Delete Order</button> */}
</div>
);
};
export default Orders;

View File

@ -0,0 +1,90 @@
import axiosInstance from "../api/axiosConfig"; // Import the global axios instance
// Get all categories
export const getAllCategories = async () => {
try {
const response = await axiosInstance.get("/categories");
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Create a new category
export const createCategory = async (categoryPayload) => {
try {
const response = await axiosInstance.post("/categories", categoryPayload);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Get a category by ID
export const getCategoryById = async (id) => {
try {
const response = await axiosInstance.get(`/categories/${id}`);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Update a category's details
export const updateCategory = async (id, categoryPayload) => {
try {
const response = await axiosInstance.patch(
`/categories/${id}`,
categoryPayload
);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Delete a category
export const deleteCategory = async (id) => {
try {
await axiosInstance.delete(`/categories/${id}`);
return; // No content to return on successful delete
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Example function to use the service
export const exampleUsage = async () => {
try {
// Example: Get all categories
const categories = await getAllCategories();
console.log("Categories:", categories);
// Example: Create a new category
const newCategory = {
name: "Electronics",
description: "Devices and gadgets",
};
const createdCategory = await createCategory(newCategory);
console.log("Created Category:", createdCategory);
// Example: Get a category by ID
const categoryId = "456";
const category = await getCategoryById(categoryId);
console.log("Category by ID:", category);
// Example: Update a category
const updatedCategoryPayload = { name: "Electronics & Gadgets" };
const updatedCategory = await updateCategory(
categoryId,
updatedCategoryPayload
);
console.log("Updated Category:", updatedCategory);
// Example: Delete a category
await deleteCategory(categoryId);
console.log("Category deleted successfully");
} catch (error) {
console.error("Error occurred while using categories service:", error);
}
};

View File

@ -0,0 +1,87 @@
import axiosInstance from "../api/axiosConfig"; // Import the global axios instance
// Get all customers
export const getAllCustomers = async () => {
try {
const response = await axiosInstance.get("/customers");
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Create a new customer
export const createCustomer = async (customerPayload) => {
try {
const response = await axiosInstance.post("/customers", customerPayload);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Get a customer by ID
export const getCustomerById = async (id) => {
try {
const response = await axiosInstance.get(`/customers/${id}`);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Update a customer's details
export const updateCustomer = async (id, customerPayload) => {
try {
const response = await axiosInstance.patch(
`/customers/${id}`,
customerPayload
);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Delete a customer
export const deleteCustomer = async (id) => {
try {
await axiosInstance.delete(`/customers/${id}`);
return; // No content to return on successful delete
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Example function to use the service
export const exampleUsage = async () => {
try {
// Example: Get all customers
const customers = await getAllCustomers();
console.log("Customers:", customers);
// Example: Create a new customer
const newCustomer = { name: "Alice Johnson", email: "alice@example.com" };
const createdCustomer = await createCustomer(newCustomer);
console.log("Created Customer:", createdCustomer);
// Example: Get a customer by ID
const customerId = "456";
const customer = await getCustomerById(customerId);
console.log("Customer by ID:", customer);
// Example: Update a customer
const updatedCustomerPayload = { email: "alice.johnson@example.com" };
const updatedCustomer = await updateCustomer(
customerId,
updatedCustomerPayload
);
console.log("Updated Customer:", updatedCustomer);
// Example: Delete a customer
await deleteCustomer(customerId);
console.log("Customer deleted successfully");
} catch (error) {
console.error("Error occurred while using customers service:", error);
}
};

View File

@ -0,0 +1,91 @@
import axiosInstance from "../api/axiosConfig";
// Get all employees
export const getAllEmployees = async () => {
try {
const response = await axiosInstance.get("/employees");
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Create a new employee
export const createEmployee = async (employeePayload) => {
try {
const response = await axiosInstance.post("/employees", employeePayload);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Get an employee by ID
export const getEmployeeById = async (id) => {
try {
const response = await axiosInstance.get(`/employees/${id}`);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Update an employee's details
export const updateEmployee = async (id, employeePayload) => {
try {
const response = await axiosInstance.patch(
`/employees/${id}`,
employeePayload
);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Delete an employee
export const deleteEmployee = async (id) => {
try {
await axiosInstance.delete(`/employees/${id}`);
return; // No content to return on successful delete
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Example function to use the employee service
export const exampleEmployeeUsage = async () => {
try {
// Example: Get all employees
const employees = await getAllEmployees();
console.log("Employees:", employees);
// Example: Create a new employee
const newEmployee = {
name: "John Doe",
email: "john.doe@example.com",
position: "Software Engineer",
};
const createdEmployee = await createEmployee(newEmployee);
console.log("Created Employee:", createdEmployee);
// Example: Get an employee by ID
const employeeId = "123";
const employee = await getEmployeeById(employeeId);
console.log("Employee by ID:", employee);
// Example: Update an employee
const updatedEmployeePayload = { position: "Senior Software Engineer" };
const updatedEmployee = await updateEmployee(
employeeId,
updatedEmployeePayload
);
console.log("Updated Employee:", updatedEmployee);
// Example: Delete an employee
await deleteEmployee(employeeId);
console.log("Employee deleted successfully");
} catch (error) {
console.error("Error occurred while using employees service:", error);
}
};

View File

@ -0,0 +1,81 @@
import axiosInstance from '../api/axiosConfig'; // Import the global axios instance
// Get all orders with their metadata
export const getAllOrders = async () => {
try {
const response = await axiosInstance.get('/orders');
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Create a new order
export const createOrder = async (orderPayload) => {
try {
const response = await axiosInstance.post('/orders', orderPayload);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Get an order by ID
export const getOrderById = async (id) => {
try {
const response = await axiosInstance.get(`/orders/${id}`);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Update an order's details
export const updateOrder = async (id, orderPayload) => {
try {
const response = await axiosInstance.patch(`/orders/${id}`, orderPayload);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Delete an order
export const deleteOrder = async (id) => {
try {
await axiosInstance.delete(`/orders/${id}`);
return; // No content to return on successful delete
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Example function to use the service
export const exampleUsage = async () => {
try {
// Example: Get all orders
const orders = await getAllOrders();
console.log('Orders:', orders);
// Example: Create a new order
const newOrder = { productId: '123', quantity: 2, status: 'pending' };
const createdOrder = await createOrder(newOrder);
console.log('Created Order:', createdOrder);
// Example: Get an order by ID
const orderId = '456';
const order = await getOrderById(orderId);
console.log('Order by ID:', order);
// Example: Update an order
const updatedOrderPayload = { status: 'shipped' };
const updatedOrder = await updateOrder(orderId, updatedOrderPayload);
console.log('Updated Order:', updatedOrder);
// Example: Delete an order
await deleteOrder(orderId);
console.log('Order deleted successfully');
} catch (error) {
console.error('Error occurred while using orders service:', error);
}
};

View File

@ -0,0 +1,102 @@
import axiosInstance from "../api/axiosConfig"; // Import the global axios instance
// Get all products with their metadata
export const getAllProducts = async () => {
try {
const response = await axiosInstance.get("/products");
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Create a new product
export const createProduct = async (productPayload) => {
try {
const response = await axiosInstance.post("/products", productPayload);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Get a product by ID
export const getProductById = async (id) => {
try {
const response = await axiosInstance.get(`/products/${id}`);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Update a product's details
export const updateProduct = async (id, productPayload) => {
try {
const response = await axiosInstance.patch(
`/products/${id}`,
productPayload
);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Delete a product
export const deleteProduct = async (id) => {
try {
await axiosInstance.delete(`/products/${id}`);
return; // No content to return on successful delete
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Get a product by SKU
export const getProductBySKU = async (sku) => {
try {
const response = await axiosInstance.get(`/products/${sku}`);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Example function to use the service
export const exampleUsage = async () => {
try {
// Example: Get all products
const products = await getAllProducts();
console.log("Products:", products);
// Example: Create a new product
const newProduct = { name: "Laptop", sku: "LPT123", price: 1200 };
const createdProduct = await createProduct(newProduct);
console.log("Created Product:", createdProduct);
// Example: Get a product by ID
const productId = "789";
const product = await getProductById(productId);
console.log("Product by ID:", product);
// Example: Update a product
const updatedProductPayload = { price: 1100 };
const updatedProduct = await updateProduct(
productId,
updatedProductPayload
);
console.log("Updated Product:", updatedProduct);
// Example: Delete a product
await deleteProduct(productId);
console.log("Product deleted successfully");
// Example: Get a product by SKU
const productSKU = "LPT123";
const productBySKU = await getProductBySKU(productSKU);
console.log("Product by SKU:", productBySKU);
} catch (error) {
console.error("Error occurred while using products service:", error);
}
};

View File

@ -0,0 +1,81 @@
import axiosInstance from "../api/axiosConfig";
// Get all users with their metadata
export const getAllUsers = async () => {
try {
const response = await axiosInstance.get("/users");
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Create a new user
export const createUser = async (userPayload) => {
try {
const response = await axiosInstance.post("/users", userPayload);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Get a user by ID
export const getUserById = async (id) => {
try {
const response = await axiosInstance.get(`/users/${id}`);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Update a user's details
export const updateUser = async (id, userPayload) => {
try {
const response = await axiosInstance.patch(`/users/${id}`, userPayload);
return response.data;
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Delete a user
export const deleteUser = async (id) => {
try {
await axiosInstance.delete(`/users/${id}`);
return; // No content to return on successful delete
} catch (error) {
throw error; // The error is handled globally by the axiosInstance
}
};
// Example function to use the service
export const exampleUsage = async () => {
try {
// Example: Get all users
const users = await getAllUsers();
console.log("Users:", users);
// Example: Create a new user
const newUser = { name: "John Doe", email: "john@example.com" };
const createdUser = await createUser(newUser);
console.log("Created User:", createdUser);
// Example: Get a user by ID
const userId = "123";
const user = await getUserById(userId);
console.log("User by ID:", user);
// Example: Update a user
const updatedUserPayload = { name: "John Smith" };
const updatedUser = await updateUser(userId, updatedUserPayload);
console.log("Updated User:", updatedUser);
// Example: Delete a user
await deleteUser(userId);
console.log("User deleted successfully");
} catch (error) {
console.error("Error occurred while using users service:", error);
}
};

148
src/styles/AddCustomer.css Normal file
View File

@ -0,0 +1,148 @@
/* AddCustomer.css */
.container {
display: flex;
flex-direction: row;
}
.main-content {
width: 100%;
display: flex;
flex-direction: column;
min-height: 100vh;
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;
}
.header-title {
display: flex;
align-items: center;
}
.back-button {
color: #0e355b;
font-size: 1.25rem; /* text-xl */
}
.title {
font-size: 1.5rem; /* text-2xl */
font-weight: 600; /* font-semibold */
color: #0e355b;
}
.skip-link {
color: #1e3a8a; /* text-blue-900 */
}
.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; /* space-y-6 */
}
.image-wrapper {
text-align: center;
}
.image-container {
display: inline-block;
position: relative;
}
.image-preview {
background-color: #d1d5db; /* bg-gray-300 */
width: 6rem; /* w-24 */
height: 6rem; /* h-24 */
border-radius: 50%;
margin: 0 auto; /* mx-auto */
overflow: hidden;
}
.no-image {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: #6b7280; /* text-gray-500 */
}
.hidden-input {
display: none;
}
.upload-button {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
background-color: #4b5563; /* bg-gray-700 */
color: white;
font-size: 0.75rem; /* text-xs */
padding: 0.25rem 0.5rem;
border-radius: 9999px; /* rounded-full */
}
.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 {
color: #4a5568; /* text-gray-800 */
font-weight: 500; /* font-medium */
}
.form-input {
width: 100%;
padding: 0.5rem;
border-bottom: 1px solid #cbd5e0; /* border-gray-400 */
&:focus {
border-color: #2d3748; /* border-gray-700 */
}
}
.form-select {
width: 100%;
padding: 0.5rem;
border-bottom: 1px solid #cbd5e0; /* border-gray-400 */
&:focus {
border-color: #2d3748; /* border-gray-700 */
}
}
.submit-button {
background-color: #0e355b;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.375rem; /* rounded-md */
}

View File

@ -0,0 +1,52 @@
/* styles.css */
.text-primary {
color: #0e355b;
}
.bg-primary {
background-color: #0e355b;
}
.text-secondary {
color: #4b5563; /* Adjusted to a more standard color */
}
.bg-light {
background-color: #ffffff;
}
.border-input {
border-bottom: 1px solid #ddd; /* Adjusted to a lighter gray */
}
.focus-border {
border-color: #e8e8e8; /* Adjusted to a more standard color */
}
.input-underline {
border: none;
border-bottom: 1px solid #ddd; /* Adjusted to a lighter gray */
}
.btn-primary {
background-color: #0e355b;
color: white;
padding: 0.5rem 1rem;
border-radius: 0.25rem;
font-weight: 600;
}
.image-upload-btn {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
background-color: #555; /* Adjusted to a more standard gray */
color: white;
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
border-radius: 9999px;
border: none; /* Ensure no border is applied */
}

View File

@ -0,0 +1,66 @@
.dashboard-container {
display: flex;
flex-direction: row;
}
.dashboard-content {
width: 100%;
display: flex;
flex-direction: column;
min-height: 100vh;
color: white;
}
.dashboard-header {
width: 100%;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.header-title {
color: #0e355b;
font-size: 2rem;
font-weight: bold;
}
.welcome-text {
color: #4b5563;
}
.dashboard-main {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1.5rem;
}
.dashboard-main-content {
width: 100%;
max-width: 400px;
text-align: center;
}
.dashboard-image {
width: 100%;
border-radius: 0.375rem;
margin-bottom: 1.5rem;
}
.btn-primary {
width: 100%;
padding: 1rem;
background-color: #0e355b;
color: #d1d5db;
border-radius: 0.375rem;
font-weight: 600;
margin-bottom: 1rem;
cursor: pointer;
}
.btn-primary:hover {
background-color: #154676;
}

View File

@ -0,0 +1,45 @@
/* .desktop-sidebar {
}
.sidebar-icon {
} */
.hover\:opacity-75:hover {
opacity: 0.75;
}
.flex {
display: flex;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.h-16 {
height: 4rem; /* 64px */
}
.w-16 {
width: 4rem; /* 64px */
}
.m-5 {
margin: 1.25rem; /* 20px */
}
.bg-white {
background-color: #ffffff;
}
.my-6 {
margin-top: 1.5rem; /* 24px */
margin-bottom: 1.5rem; /* 24px */
}

View File

@ -1,94 +1,94 @@
.customer-list {
padding: 20px;
background-color: #ffffff;
/* color: #09090B; */
border-radius: 8px;
max-width: 800px;
margin: 0 auto;
}
.customer-list h1 {
color: #0e355b;
text-align: center;
}
.customer-actions {
display: flex;
justify-content: flex-end;
margin-bottom: 20px;
}
.add-customer-button {
padding: 10px 20px;
background-color: #0e355b;
/* color: #09090B; */
border: none;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
}
.add-customer-button:hover {
background-color: #154676;
}
.customer-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.customer-table th,
.customer-table td {
border: 1px solid #ffffff;
padding: 10px;
text-align: left;
}
.customer-table th {
background-color: #09090B;
}
.customer-table tbody tr:nth-child(odd) {
background-color: #09090B;
}
.customer-table tbody tr:nth-child(even) {
background-color: #09090B;
}
.customer-table tbody tr:hover {
background-color: #0e355b;
}
.edit-button {
padding: 5px 10px;
background-color: #0e355b;
color: #ffffff;
border: none;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
}
.edit-button:hover {
background-color: #154676;
}
.input-underline {
background-color: transparent;
border: none;
border-bottom: 1px solid #b3b3b3;
color: #e8e8e8;
width: 100%;
padding: 0.5rem 0;
margin-bottom: 20px;
font-size: 16px;
outline: none;
}
.input-underline:focus {
border-bottom-color: #4b5563; /* Orange underline on focus */
}
.customer-list {
padding: 20px;
background-color: #ffffff;
/* color: #09090B; */
border-radius: 8px;
max-width: 800px;
margin: 0 auto;
}
.customer-list h1 {
color: #0e355b;
text-align: center;
}
.customer-actions {
display: flex;
justify-content: flex-end;
margin-bottom: 20px;
}
.add-customer-button {
padding: 10px 20px;
background-color: #0e355b;
/* color: #09090B; */
border: none;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
}
.add-customer-button:hover {
background-color: #154676;
}
.customer-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.customer-table th,
.customer-table td {
border: 1px solid #ffffff;
padding: 10px;
text-align: left;
}
.customer-table th {
background-color: #ffffff;
}
.customer-table tbody tr:nth-child(odd) {
background-color: #ffffff;
}
.customer-table tbody tr:nth-child(even) {
background-color: #ffffff;
}
.customer-table tbody tr:hover {
background-color: #0e355b;
}
.edit-button {
padding: 5px 10px;
background-color: #0e355b;
color: #ffffff;
border: none;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
}
.edit-button:hover {
background-color: #154676;
}
.input-underline {
background-color: transparent;
border: none;
border-bottom: 1px solid #b3b3b3;
color: #e8e8e8;
width: 100%;
padding: 0.5rem 0;
margin-bottom: 20px;
font-size: 16px;
outline: none;
}
.input-underline:focus {
border-bottom-color: #4b5563; /* Orange underline on focus */
}

View File

@ -0,0 +1,74 @@
/* CustomerMeasurements.css */
/* Container styling */
.container {
display: flex;
}
/* Content styling */
.content {
flex: 1;
}
/* Form container styling */
.form-container {
max-width: 40rem; /* 640px */
margin: 0 auto;
padding: 1.5rem; /* 24px */
}
/* Header styling */
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem; /* 16px */
}
/* Skip link styling */
.skip-link {
color: #0e355b; /* Tailwind's blue-600 */
text-decoration: none;
}
/* Section title styling */
.section-title {
font-size: 1.125rem; /* 18px */
font-weight: 500;
margin-bottom: 0.5rem; /* 8px */
}
/* Input field styling */
.input {
width: 100%;
border: none;
/* Tailwind's gray-400 */
outline: none;
padding: 0.5rem 0; /* 8px top and bottom */
transition: border-color 0.3s ease;
}
.input:focus {
border-bottom-color: #0e355b; /* Custom blue color for focus */
}
/* Button container styling */
.button-container {
display: flex;
justify-content: flex-end;
}
/* Submit button styling */
.submit-button {
background-color: #0e355b;
color: white;
padding: 0.5rem 1rem; /* 8px top and bottom, 16px left and right */
border-radius: 0.375rem; /* 6px */
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
}
.submit-button:hover {
background-color: #0e355b; /* Darker shade for hover */
}

125
src/styles/Customize.css Normal file
View File

@ -0,0 +1,125 @@
/* Customize.css */
body {
font-family: 'Helvetica', 'Arial', sans-serif;
}
.flex {
display: flex;
}
.h-screen {
height: 100vh;
}
.bg-white {
background-color: #fff;
}
.p-4 {
padding: 1rem;
}
.p-6 {
padding: 1.5rem;
}
.p-8 {
padding: 2rem;
}
.shadow-lg {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.rounded-lg {
border-radius: 0.5rem;
}
.w-half{
width: 33.333333%;
}
.w-full {
width: 100%;
}
.h-auto {
height: auto;
}
.mt-1 {
margin-top: 0.25rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mr-4 {
margin-right: 1rem;
}
.text-lg {
font-size: 1.125rem;
}
.text-2xl {
font-size: 1.5rem;
}
.text-sm {
font-size: 0.875rem;
}
.text-gray-700 {
color: #4a5568;
}
.font-medium {
font-weight: 500;
}
.items-center {
align-items: center;
}
.items-stretch {
align-items: stretch;
}
.flex-grow {
flex-grow: 1;
}
.bg-blue-600 {
background-color: #3182ce;
}
.bg-blue-700 {
background-color: #2b6cb0;
}
.text-white {
color: #fff;
}
.border {
border-width: 1px;
}
.border-gray-300 {
border-color: #d2d6dc;
}
.rounded-md {
border-radius: 0.375rem;
}
.hover\:bg-blue-700:hover {
background-color: #2b6cb0;
}
.block {
display: block;
}

View File

@ -0,0 +1,66 @@
.dashboard-container {
display: flex;
flex-direction: row;
}
.dashboard-content {
width: 100%;
display: flex;
flex-direction: column;
min-height: 100vh;
color: white;
}
.dashboard-header {
width: 100%;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.header-title {
color: #0e355b;
font-size: 2rem;
font-weight: bold;
}
.welcome-text {
color: #4b5563;
}
.dashboard-main {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 1.5rem;
}
.dashboard-main-content {
width: 100%;
max-width: 400px;
text-align: center;
}
.dashboard-image {
width: 100%;
border-radius: 0.375rem;
margin-bottom: 1.5rem;
}
.btn-primary {
width: 100%;
padding: 1rem;
background-color: #0e355b;
color: #d1d5db;
border-radius: 0.375rem;
font-weight: 600;
margin-bottom: 1rem;
cursor: pointer;
}
.btn-primary:hover {
background-color: #154676;
}

View File

@ -0,0 +1,62 @@
/* .desktop-sidebar {
}
.sidebar-icon {
} */
.hover\:opacity-75:hover {
opacity: 0.75;
}
.flex {
display: flex;
}
.flex-col {
flex-direction: column;
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.h-16 {
height: 4rem; /* 64px */
}
.w-16 {
width: 4rem; /* 64px */
}
.mb-5 {
margin-bottom: 1.25rem; /* 20px */
}
.mt-4 {
margin-top: 1rem; /* 16px */
}
.bg-white {
background-color: #ffffff;
}
.flex-grow {
flex-grow: 1;
}
.my-6 {
margin-top: 1.5rem; /* 24px */
margin-bottom: 1.5rem; /* 24px */
}
.mb-4 {
margin-bottom: 1rem; /* 16px */
}

View File

@ -0,0 +1,24 @@
.custom-select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background: transparent;
border: 1px solid #d1d5db; /* Tailwind's border-gray-300 */
border-radius: 0.375rem; /* Tailwind's rounded */
padding: 0.5rem 2.5rem 0.5rem 0.75rem; /* Adjust padding for arrow */
font-size: 1rem; /* Tailwind's text-sm */
}
.custom-select::after {
content: '▼'; /* Unicode arrow */
position: absolute;
top: 50%;
right: 0.75rem; /* Adjust as needed */
transform: translateY(-50%);
pointer-events: none;
color: #6b7280; /* Tailwind's text-gray-700 */
}
.select-container {
position: relative;
display: inline-block;
width: 100%; /* Ensure full width */
}

107
src/styles/ProductList.css Normal file
View File

@ -0,0 +1,107 @@
.product-list {
padding: 20px;
background-color: #ffffff;
border-radius: 8px;
max-width: 1200px;
margin: 0 auto;
}
.product-list h1 {
color: #0e355b;
text-align: left;
margin-bottom: 20px;
}
.product-actions {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.filter-button {
padding: 10px 20px;
background-color: #0e355b;
color: #ffffff;
border: none;
cursor: pointer;
border-radius: 5px;
transition: background-color 0.3s;
}
.filter-button:hover {
background-color: #154676;
}
.product-grid {
display: grid;
grid-template-columns: repeat(1, 1fr);
gap: 20px;
}
@media (min-width: 640px) {
.product-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 768px) {
.product-grid {
grid-template-columns: repeat(3, 1fr);
}
}
.product-row {
display: flex;
flex-direction: column;
border: 1px solid #e8e8e8;
border-radius: 8px;
overflow: hidden;
transition: box-shadow 0.3s;
}
.product-row:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.product-row img {
width: 100%;
height: auto;
}
.product-row .product-name {
font-size: 1.5rem;
font-weight: 600;
color: #0e355b;
padding: 10px 20px;
text-align: center;
}
.product-row .view-products {
font-size: 1rem;
color: #ffffff;
background-color: #0e355b;
padding: 10px 20px;
text-align: center;
cursor: pointer;
transition: background-color 0.3s;
}
.product-row .view-products:hover {
background-color: #154676;
}
.customize-button {
background-color: #0e355b;
color: #d1d5db;
border: none;
border-radius: 4px;
padding: 8px 16px;
cursor: pointer;
text-align: center;
}
.customize-button:hover {
background-color: #154676;
}

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',
};

View File

@ -0,0 +1,59 @@
import React, { useState } from 'react';
import axiosInstance from '../utils/axiosInstance';
const SubmitForm = () => {
const [formData, setFormData] = useState({ name: '', email: '' });
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(false);
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value,
});
};
const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);
try {
const response = await axiosInstance.post('/submit-endpoint', formData);
console.log(response.data);
setSuccess(true);
setLoading(false);
} catch (err) {
setError(err);
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Name"
required
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
required
/>
<button type="submit" disabled={loading}>
{loading ? 'Submitting...' : 'Submit'}
</button>
{success && <p>Form submitted successfully!</p>}
{error && <p>Error submitting form</p>}
</form>
);
};
export default SubmitForm;

38
tailwind.config.cjs Normal file
View File

@ -0,0 +1,38 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.{html,js,jsx}',
],
theme: {
extend: {
colors: {
primary: '#0e355b',
'primary-dark': '#154676',
secondary: '#ffffff',
'primary-text': '#0e355b',
'secondary-text': '#4b5563',
'button-text': '#d1d5db',
},
fontFamily: {
sans: ['"Nunito Sans"', 'sans-serif'],
},
fontSize: {
base: '14px',
'h1': '24px',
'p': '16px',
'input': '16px',
'button': '18px',
},
screens: {
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
'2xl': '1536px',
},
},
},
plugins: [],
};

View File

@ -1,10 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

View File

@ -1,7 +1,12 @@
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
});

4014
yarn.lock

File diff suppressed because it is too large Load Diff