Compare commits

..

No commits in common. "main" and "branch-login" have entirely different histories.

72 changed files with 2346 additions and 24876 deletions

12
.env
View File

@ -1,12 +0,0 @@
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,12 +11,6 @@
"email": "employee@gmail.com", "email": "employee@gmail.com",
"password": "123", "password": "123",
"role": "employee" "role": "employee"
},
{
"id": "de4c"
},
{
"id": "5996"
} }
], ],
"health-check": { "health-check": {
@ -25,83 +19,29 @@
"admin": [ "admin": [
{ {
"id": "1", "id": "1",
"firstname": "Admin User", "name": "Admin User",
"lastname": "last ",
"email": "adminuser@example.com" "email": "adminuser@example.com"
} }
], ],
"employees": [ "employee": [
{ {
"id": "1", "id": "1",
"firstname": "Employee User", "name": "Employee User",
"lastname": "last ",
"email": "employeeuser@example.com" "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": [ "products": [
{ {
"ID": "1", "id": "1",
"name": "Men's T-Shirt", "name": "Product 1",
"sku": "TS-MEN-001-BL-XL", "category": "Category 1",
"short_description": "A comfortable men's clothing fibers", "price": 100
"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", "id": "2",
"name": "Women's Jacket", "name": "Product 2",
"sku": "JK-WOM-001-RE-M", "category": "Category 2",
"short_description": "Stylish women's jacket", "price": 200
"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": [ "categories": [
@ -117,116 +57,61 @@
"customers": [ "customers": [
{ {
"id": "1", "id": "1",
"firstname": "vitthal", "name": "Customer 1",
"lastname": "patil",
"email": "customer1@example.com", "email": "customer1@example.com",
"phone": "1234567890" "phone": "1234567890"
}, },
{ {
"id": "2", "id": "2",
"firstname": "rushi", "name": "Customer 2",
"lastname": "shelke",
"email": "customer2@example.com", "email": "customer2@example.com",
"phone": "0987654321" "phone": "0987654321"
}, },
{ {
"id": "7779", "name": "Suraj Birewar",
"firstname": "Suraj", "age": "24",
"lastname": "Birewar", "gender": "Male",
"age": "22",
"gender": "male",
"email": "surajbirewar001@gmail.com", "email": "surajbirewar001@gmail.com",
"address": "Bavdhan, Pune",
"mobile": "07559393995", "mobile": "07559393995",
"address": { "id": "3"
"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"
}, },
{ {
"id": "2", "name": "rahul",
"name": "Los Angeles" "age": "21",
"gender": "male",
"email": "rahul001@gmail.com",
"address": "Bavdhan, Pune",
"mobile": "7559393995",
"id": "4"
}, },
{ {
"id": "3", "name": "atharv",
"name": "Chicago" "age": "19",
"gender": "male",
"email": "atharv@gmail.com",
"address": "baner pune ",
"mobile": "784365873",
"id": "5"
}, },
{ {
"id": "4", "id": "3682",
"name": "Houston" "name": "dikshant",
"age": "23",
"gender": "male",
"email": "dikshant001@gmail.com",
"address": "Bavdhan, Pune",
"mobile": "45654665645"
}, },
{ {
"id": "5", "id": "377d",
"name": "Phoenix" "firstname": "tejas",
}, "lastname": "chari",
{ "age": "28",
"id": "333e", "gender": "male",
"name": "las vegas" "email": "tejas001@gmail.com",
} "address": " Baner, Pune",
], "mobile": "8675493099"
"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,8 +1,7 @@
import axios from 'axios'; import axios from 'axios';
const axiosInstance = axios.create({ const axiosInstance = axios.create({
baseURL: 'http://localhost:5000', baseURL: 'http://localhost:5000'
timeout: 10000,
}); });
export default axiosInstance; export default axiosInstance;

18164
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,45 +16,24 @@
"proxy": "http://localhost:5000", "proxy": "http://localhost:5000",
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.3.7", "@ant-design/icons": "^5.3.7",
"@craco/craco": "^7.1.0",
"@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/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", "antd": "^5.18.2",
"axios": "^1.7.2", "axios": "^1.7.2",
"classnames": "^2.5.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.25.1", "react-router-dom": "^6.23.1"
"react-toastify": "^10.0.5"
}, },
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.0.0",
"@types/react": "^18.2.66", "@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22", "@types/react-dom": "^18.2.22",
"@vitejs/plugin-react": "^4.2.1", "@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^9.8.8", "autoprefixer": "^10.4.19",
"axios-mock-adapter": "^1.22.0",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1", "eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6", "eslint-plugin-react-refresh": "^0.4.6",
"jest": "^27.5.1",
"json-server": "^1.0.0-beta.1", "json-server": "^1.0.0-beta.1",
"postcss": "^7.0.39", "postcss": "^8.4.38",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17", "tailwindcss": "^3.4.4",
"vite": "^5.3.4" "vite": "^5.2.0"
},
"jest": {
"testEnvironment": "jsdom",
"moduleNameMapper": {
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
}
} }
} }

View File

@ -1,81 +1,44 @@
import React from "react"; import React, { useContext } from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate } from "react-router-dom"; import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
import { Layout } from "antd"; import { Layout } from 'antd';
import LoginPage from "./components/Auth/LoginPage"; import LoginPage from './components/Auth/LoginPage';
import ResetPasswordRequest from "./components/Auth/ResetPasswordRequest"; import AdminDashboard from './components/admin/AdminDashboard';
import AdminDashboard from "./pages/AdminDashboard"; import EmployeeDashboard from './components/employee/EmployeeDashboard';
import EmployeeDashboard from "./pages/EmployeeDashboard"; import CustomerList from './components/customer/CustomerList';
import CustomerList from "./components/customer/CustomerList"; import AddCustomer from './components/customer/AddCustomer';
import AddCustomer from "./components/customer/AddCustomer"; import Sidebar from './components/sidebar/Sidebar';
import Sidebar from "./components/sidebar/Sidebar"; import CustomerMeasurements from './components/customer/CustomerMeasurements';
import CustomerMeasurements from "./components/customer/CustomerMeasurements"; import { AuthProvider, AuthContext } from './contexts/AuthContext';
import { AuthProvider, AuthContext } from "./contexts/AuthContext"; import EmployeeList from './components/admin/EmployeeList';
import EmployeeList from "./components/admin/EmployeeList"; import AddEmployee from './components/admin/AddEmployee';
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 { Content } = Layout;
const App = () => { const App = () => {
const { user } = { name: "Suraj" }; // useContext(AuthContext); const { user } = { name: 'Suraj' }; // useContext(AuthContext);
return ( return (
<Router> <div className="min-h-screen flex w-full">
<RefineKbarProvider> <Content className="flex-1 text-white">
<Refine>
<AuthProvider> <AuthProvider>
<div className="min-h-screen flex w-full"> <Router>
<Content className="flex-1 text-white">
<Routes> <Routes>
<Route exact path="/" element={<LoginPage />} /> <Route exact path="/" element={<LoginPage />} />
<Route path="/reset-password" element={<ResetPasswordRequest />} /> <Route path="/employee/home" element={<EmployeeDashboard />} /> {/* Add HomePage route */}
<Route path="/employee/home" element={<EmployeeDashboard />} />
<Route path="/admin" element={<PrivateRoute><AdminDashboard /></PrivateRoute>} /> <Route path="/admin" element={<PrivateRoute><AdminDashboard /></PrivateRoute>} />
<Route path="/employee" element={<PrivateRoute><EmployeeDashboard /></PrivateRoute>} /> <Route path="/employee" element={<PrivateRoute><EmployeeDashboard /></PrivateRoute>} />
<Route path="/customers" element={<PrivateRoute><CustomerList /></PrivateRoute>} /> <Route path="/customers" element={<PrivateRoute><CustomerList /></PrivateRoute>} />
<Route path="/add-customer" element={<PrivateRoute><AddCustomer /></PrivateRoute>} /> <Route path="/add-customer" element={<PrivateRoute><AddCustomer /></PrivateRoute>} />
<Route path="/employee/create-order" element={<CustomerList />} /> <Route path="/employee/create-order" element={<CustomerList />} />
<Route path="/measurements" element={<CustomerMeasurements />} /> <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="/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> </Routes>
</Content> </Router>
</div> </AuthProvider>
</AuthProvider> </Content>
</Refine> </div>
</RefineKbarProvider>
<ToastContainer /> {/* Added ToastContainer for displaying notifications */}
</Router>
); );
}; };

View File

@ -1,49 +1,20 @@
import axios from 'axios'; 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({ const axiosInstance = axios.create({
baseURL, baseURL: '/api',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
}); });
// Request interceptor to add JWT token
axiosInstance.interceptors.request.use((config) => { axiosInstance.interceptors.request.use((config) => {
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
if (token) { if (token) {
config.headers['Authorization'] = `Bearer ${token}`; config.headers.Authorization = `Bearer ${token}`;
} }
return config; return config;
}, error => { }, (error) => {
// Handle request error
console.error('Request error:', error);
return Promise.reject(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; export default axiosInstance;

View File

@ -1,34 +0,0 @@
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

@ -1,71 +0,0 @@
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();
}
});
});

View File

@ -1,52 +0,0 @@
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,6 +5,7 @@ body, html {
padding: 0; padding: 0;
font-family: 'Nunito Sans', sans-serif; font-family: 'Nunito Sans', sans-serif;
background-color: #ffffff; background-color: #ffffff;
/* color: #fff; */
} }
.login-page { .login-page {
@ -14,15 +15,28 @@ body, html {
min-height: 100vh; min-height: 100vh;
padding: 4rem; padding: 4rem;
background-color: #ffffff; background-color: #ffffff;
position: relative;
} }
.logo { .login-container {
position: absolute; /* background-color: #222; */
top: 1rem; border-radius: 8px;
left: 1rem; padding: 40px;
width: 100px; box-shadow: 0 0 10px #d1d5db;
height: auto; 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;
} }
h1 { h1 {
@ -37,20 +51,44 @@ p {
text-align: center; text-align: center;
} }
input { form {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
label {
display: none;
}
/* input[type="email"],
input[type="password"] {
width: 100%; width: 100%;
padding: 12px; padding: 12px;
margin-bottom: 20px; margin-bottom: 20px;
border: 1px solid #B3B3B3; border: 1px solid #B3B3B3;
border-radius: 4px; border-radius: 4px;
font-size: 16px; font-size: 16px;
background-color: #e8e8e8; background-color: #09090B;
color: #4b5563; color: #fff;
box-sizing: border-box; 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:focus { input[type="email"]:focus,
border-color: #0e355b; input[type="password"]:focus {
border-color: #4b5563;
outline: none; outline: none;
} }
@ -67,6 +105,10 @@ input:focus {
text-decoration: none; text-decoration: none;
} }
/* .forgot-password-container a:hover {
text-decoration: underline;
} */
.login-button { .login-button {
width: 100%; width: 100%;
padding: 15px 30px; padding: 15px 30px;
@ -81,3 +123,15 @@ input:focus {
.login-button:hover { .login-button:hover {
background-color: #154676; 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 React, { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import axiosInstance from '../../api/axiosConfig'; import axiosInstance from '../../api/axiosConfig';
import BrookslogoIcon from '../../assets/thob-data/BrookslogoIcon.svg'; import './LoginPage.css';
const LoginPage = () => { const LoginPage = () => {
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
@ -12,19 +12,19 @@ const LoginPage = () => {
const handleLogin = async (e) => { const handleLogin = async (e) => {
e.preventDefault(); e.preventDefault();
setError(''); setError('');
try { try {
const response = await axiosInstance.get('http://localhost:5000/authlogin'); const response = await axiosInstance.get('http://localhost:5000/authlogin');
const user = response.data.find(user => user.email === email && user.password === password); const user = response.data.find(user => user.email === email && user.password === password);
if (user) { if (user) {
localStorage.setItem("loggedInUser", JSON.stringify(user));
if (user.role === 'admin') { if (user.role === 'admin') {
navigate('/admin'); navigate('/admin');
} else if (user.role === 'employee') { } else if (user.role === 'employee') {
navigate('/employee'); navigate('/employee');
} else { } else {
setError('Invalid role.'); setError('Invalid credentials');
} }
} else { } else {
setError('Invalid credentials'); setError('Invalid credentials');
@ -39,44 +39,44 @@ const LoginPage = () => {
} }
} }
}; };
return ( return (
<div className="flex flex-col min-h-screen bg-secondary p-4 relative"> <div className="flex flex-row min-h-screen justify-center items-center">
<img src={BrookslogoIcon} alt="Logo" className="absolute top-6 left-6 w-24" /> <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="flex flex-col items-center justify-center flex-1 px-4 py-6"> <div className="w-full">
<h1 className="text-primary-text text-2xl font-bold mb-4">welcome back</h1> <h1>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> <p>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>} {error && <p className="error-message">{error}</p>}
<div className="flex flex-col space-y-4 w-full max-w-sm"> <form onSubmit={handleLogin} className="space-y-4">
<input <label htmlFor="email" className="block"></label>
type="email" <input
id="email" type="email"
name="email" id="email"
value={email} name="email"
onChange={(e) => setEmail(e.target.value)} value={email}
placeholder="john123@domain.com" onChange={(e) => setEmail(e.target.value)}
required placeholder="john123@domain.com"
className="w-full p-3 border border-gray-300 rounded-md text-gray-700 focus:border-primary focus:outline-none" required
/> className="input-field"
<input />
type="password" <label htmlFor="password" className="block"></label>
id="password" <input
name="password" type="password"
value={password} id="password"
onChange={(e) => setPassword(e.target.value)} name="password"
placeholder="Enter your password" value={password}
required onChange={(e) => setPassword(e.target.value)}
className="w-full p-3 border border-gray-300 rounded-md text-gray-700 focus:border-primary focus:outline-none" placeholder="Enter your password"
/> required
<div className="flex justify-end"> className="input-field"
<a href="/reset-password" className="text-primary-text">forgot password?</a> />
</div> <div className="forgot-password-container">
<button <a href="#">Forgot password?</a>
onClick={handleLogin} </div>
className="w-full py-3 bg-primary text-button-text rounded-md text-lg font-semibold hover:bg-primary-dark" <button type="submit" className="login-button">
> Login
login </button>
</button> </form>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,49 +0,0 @@
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

@ -1,12 +0,0 @@
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,87 +1,29 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { getUsers, createUser, updateUser, deleteUser } from '../api/userService'; import axiosInstance from '../api/axiosInstance';
const UserList = () => { const UsersList = () => {
const [users, setUsers] = useState([]); const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [newUser, setNewUser] = useState({ email: '', roles: '' });
useEffect(() => { useEffect(() => {
const fetchUsers = async () => { axiosInstance.get('/users')
try { .then(response => {
const userData = await getUsers(); setUsers(response.data);
setUsers(userData); })
} catch (error) { .catch(error => {
setError(error); console.error('There was an error fetching the users!', 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 ( return (
<div> <div>
<h2>User List</h2> <h1>Users List</h1>
<ul> <ul>
{users.map((user) => ( {users.map(user => (
<li key={user.ID}> <li key={user.id}>{user.name}</li>
{user.Email} - {user.Roles.join(', ')}
<button onClick={() => handleUpdateUser(user.ID, {/* Updated data */})}>Update</button>
<button onClick={() => handleDeleteUser(user.ID)}>Delete</button>
</li>
))} ))}
</ul> </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> </div>
); );
}; };
export default UserList; export default UsersList;

View File

@ -1,33 +1,7 @@
import React, { useState, useEffect } from 'react'; import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom'; 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 Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig'; 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 AddEmployee = () => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -37,65 +11,22 @@ const AddEmployee = () => {
age: '', age: '',
gender: '', gender: '',
email: '', email: '',
address: '',
mobile: '', 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 handleInputChange = (e) => {
const { name, value } = e.target; const { name, value } = e.target;
setEmployeeData((prevData) => ({ setEmployeeData({
...prevData, ...employeeData,
[name]: value, [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) => { const handleAddEmployeeSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
const formData = new FormData();
Object.keys(employeeData).forEach((key) => {
formData.append(key, employeeData[key]);
});
try { try {
const data = await createEmployee(employeeData); await axiosInstance.post('/employees', employeeData);
console.log('Employee added successfully:', data);
navigate('/employees'); navigate('/employees');
} catch (error) { } catch (error) {
console.error('Error adding employee:', error.response || error.message); console.error('Error adding employee:', error.response || error.message);
@ -103,170 +34,107 @@ const AddEmployee = () => {
}; };
return ( return (
<div className={CLASSES.container}> <div className="flex flex-row">
<Sidebar /> <Sidebar />
<div className={CLASSES.mainContent}> <div className="w-full flex flex-col min-h-screen text-black">
<div className={CLASSES.innerContent}> <div className="min-h-screen text-gray-800 p-6">
<div className={CLASSES.header}> <div className="flex justify-between items-center mb-6">
<div className={CLASSES.headerTitle}> <h1 className="text-2xl font-semibold text-blue-900">Creating Employee Account</h1>
<Link to="/employees" className={CLASSES.backButton}> <Link to="/" className="text-blue-900">Skip for now</Link>
<FontAwesomeIcon icon={faChevronLeft} />
</Link>
<h1 className={CLASSES.title}>create employee account</h1>
</div>
</div> </div>
<div className={CLASSES.formGrid}> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className={CLASSES.imageSection}> <div className="space-y-6">
<div className={CLASSES.imageWrapper}> <div className="text-center">
<div className={CLASSES.imageContainer}> <div className="inline-block relative">
<div className={CLASSES.imagePreview}> <div className="bg-gray-300 w-24 h-24 rounded-full mx-auto"></div>
{imagePreview ? ( <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">
<img Add Employee Image
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> </button>
</div> </div>
</div> </div>
</div> </div>
<div className={CLASSES.formSection}> <div className="md:col-span-2 space-y-4">
<form onSubmit={handleAddEmployeeSubmit}> <form onSubmit={handleAddEmployeeSubmit}>
<div> <div>
<label className={CLASSES.formGroup}>first name:</label> <label className="block text-gray-800 font-medium">Employee Firstname:</label>
<input <input
type="text" type="text"
name="firstname" name="firstname"
value={employeeData.firstname} value={employeeData.firstname}
onChange={handleInputChange} onChange={handleInputChange}
className={CLASSES.formInput} className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
/> />
</div> </div>
<div> <div>
<label className={CLASSES.formGroup}>last name:</label> <label className="block text-gray-800 font-medium">Employee Lastname:</label>
<input <input
type="text" type="text"
name="lastname" name="lastname"
value={employeeData.lastname} value={employeeData.lastname}
onChange={handleInputChange} onChange={handleInputChange}
className={CLASSES.formInput} className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
/> />
</div> </div>
<div> <div>
<label className={CLASSES.formGroup}>age:</label> <label className="block text-gray-800 font-medium">Age:</label>
<input <input
type="number" type="number"
name="age" name="age"
value={employeeData.age} value={employeeData.age}
onChange={handleInputChange} onChange={handleInputChange}
className={CLASSES.formInput} className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
/> />
</div> </div>
<div>
<div className={CLASSES.formGroup}> <label className="block text-gray-800 font-medium">Gender:</label>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
className={CLASSES.formInput}
value={employeeData.email}
onChange={handleInputChange}
/>
</div>
<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}
/>
</div>
<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 <select
id="city_id" name="gender"
name="address.city_id" value={employeeData.gender}
className={CLASSES.formSelect}
value={employeeData.address.city_id}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
> >
<option value="">Select City</option> <option value="" disabled>Select Gender</option>
{cities.map((city) => ( <option value="male">Male</option>
<option key={city.id} value={city.id}> <option value="female">Female</option>
{city.name} <option value="other">Other</option>
</option>
))}
</select> </select>
</div> </div>
<div>
<button type="submit" className={CLASSES.submitButton}> <label className="block text-gray-800 font-medium">Email:</label>
add employee <input
</button> type="email"
name="email"
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>
<input
type="text"
name="mobile"
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>
</form> </form>
</div> </div>
</div> </div>

View File

@ -0,0 +1,53 @@
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

@ -0,0 +1,89 @@
/* 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,33 +2,35 @@ import React, { useState, useEffect, useCallback } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar'; import Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig'; import axiosInstance from '../../api/axiosConfig';
// import EmployeeCards from './EmployeeCards';
// import PropTypes from 'prop-types';
const EmployeeList = ({ className = '' }) => { const EmployeeList = ({ className = '' }) => {
const [employees, setEmployees] = useState([]); const [employees, setEmployees] = useState([]);
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
// Fetch the employees data from the JSON file
getEmployees(); getEmployees();
}, []); }, []);
const getEmployees = async () => { const getEmployees = async () => {
try { try {
const response = await axiosInstance.get('http://localhost:5000/employees'); const response = await axiosInstance.get('http://localhost:5000/employees');
let employees_data = response.data; let employees_data = response.data; // List of employees
setEmployees(employees_data); setEmployees(employees_data);
} catch (error) { } catch (error) {
console.error('Error fetching employees:', error.response || error.message); throw error;
} }
}; };
const handleAddEmployee = () => { const handleAddEmployee = () => {
navigate('/add-employee'); navigate('/add-employee');
}; };
const onEmployeeClick = useCallback(() => { // const onEmployeeCardsClick = useCallback(() => {
navigate('/employee-profile'); // navigate('/employee-profile');
}, [navigate]); // }, [navigate]);
return ( return (
<div className="flex flex-row min-h-screen"> <div className="flex flex-row min-h-screen">
@ -36,12 +38,12 @@ const EmployeeList = ({ className = '' }) => {
<div className="flex-grow p-6 bg-gray-100"> <div className="flex-grow p-6 bg-gray-100">
<div className="bg-white p-6 rounded-lg shadow"> <div className="bg-white p-6 rounded-lg shadow">
<div className="flex justify-between items-center mb-6"> <div className="flex justify-between items-center 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 <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={handleAddEmployee} onClick={handleAddEmployee}
> >
add new employee Add New Employee
</button> </button>
</div> </div>
<div className="flex items-center mb-4"> <div className="flex items-center mb-4">
@ -59,9 +61,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" 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> </svg>
<span className="text-sm text-gray-600">filter</span> <span className="text-sm text-gray-600">Filter</span>
</div> </div>
<p className="text-sm text-gray-600 mb-4">total employees: ({employees.length})</p> <p className="text-sm text-gray-600 mb-4">Total Employees ({employees.length})</p>
<table className="w-full bg-white"> <table className="w-full bg-white">
<thead> <thead>
<tr className="text-left text-xs uppercase text-gray-600 border-b"> <tr className="text-left text-xs uppercase text-gray-600 border-b">
@ -78,12 +80,12 @@ const EmployeeList = ({ className = '' }) => {
<td className="py-2 text-gray-800">{employee.firstname} {employee.lastname}</td> <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">{employee.email}</td>
<td className="py-2 text-gray-800"> <td className="py-2 text-gray-800">
<span <button
className="text-#4b5563 underline cursor-pointer hover:text-#0e355b transition" className="text-#4b5563 underline"
onClick={onEmployeeClick} // onClick={onEmployeeCardsClick}
> >
view details View Details
</span> </button>
</td> </td>
</tr> </tr>
))} ))}
@ -95,4 +97,8 @@ const EmployeeList = ({ className = '' }) => {
); );
}; };
// EmployeeList.propTypes = {
// className: PropTypes.string,
// };
export default EmployeeList; export default EmployeeList;

View File

@ -1,157 +0,0 @@
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,51 +1,124 @@
import React from 'react'; import React from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar'; import ProductRow from "./ProductRow";
import '../../styles/ProductList.css'; import PropTypes from "prop-types";
const CategoryList = () => { const CategoryList = ({ className = "" }) => {
const navigate = useNavigate(); const navigate = useNavigate();
// Updated categories array with names and specifications const handleNavigation = (path) => {
const categories = [ navigate(path);
{ 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 ( return (
<div className="flex h-screen"> <div
{/* Sidebar */} 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}`}
<Sidebar /> >
<div className="flex flex-col items-start justify-start py-0 pr-[33px] pl-0">
{/* Main content */} <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-grow flex flex-col"> <div className="flex flex-col items-start justify-start gap-[146.5px]">
<div className="bg-white p-4 flex items-center"> <div className="w-10 h-[147px] relative">
<h1 className="ml-4 text-2xl font-medium text-gray-700">Categories</h1> <img
</div> className="absolute h-[27.21%] w-full top-[0%] right-[0%] bottom-[72.79%] left-[0%] max-w-full overflow-hidden max-h-full"
<div className="flex-grow p-8"> loading="lazy"
<div className="grid grid-cols-2 gap-8"> alt=""
{categories.map((category, index) => ( src="/brooks-logo.svg"
<div />
key={index} <div className="absolute top-[70px] left-[calc(50%_-_20px)] w-10 h-[77px] hidden">
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" <div className="absolute top-[0px] left-[0px] w-full h-full hidden">
onClick={() => handleClick(category.name.toLowerCase())} // Updated to handle lowercase category names <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]" />
<img src={category.image} alt={category.name} className="w-48 h-48 object-cover" /> </div>
<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 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>
<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>
</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> </div>
</div> </div>
</div> </div>
); );
}; };
CategoryList.propTypes = {
className: PropTypes.string,
};
export default CategoryList; export default CategoryList;

View File

@ -1,66 +0,0 @@
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,8 +1,7 @@
import React, { useMemo } from 'react'; import { useMemo } from "react";
import PropTypes from 'prop-types'; import PropTypes from "prop-types";
import '../../styles/ProductList.css';
const ProductRow = ({ className = "", image17, shirts, price, onClick, propLeft, propTop }) => { const ProductRow = ({ className = "", image17, shirts, propLeft, propTop }) => {
const productRowStyle = useMemo(() => { const productRowStyle = useMemo(() => {
return { return {
left: propLeft, left: propLeft,
@ -12,42 +11,37 @@ const ProductRow = ({ className = "", image17, shirts, price, onClick, propLeft,
return ( return (
<div <div
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}`} 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}`}
style={productRowStyle} style={productRowStyle}
> >
<img <img
className="w-full h-full object-cover" className="self-stretch flex-1 relative max-w-full overflow-hidden max-h-full object-cover"
loading="lazy" loading="lazy"
alt={shirts} alt=""
src={image17} src={image17}
/> />
<div className="w-full flex flex-col items-start py-0 px-5"> <div className="self-stretch flex flex-row items-start justify-center py-0 pr-[21px] pl-5">
<div className="text-xl mb-2 text-gray-700"> <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]">
{shirts} {shirts}
</div> </div>
<div className="text-lg mb-4 text-gray-500"> </div>
${price} <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> </div>
</div> </div>
{/* Customize Button */}
<button
className="customize-button w-full py-2 px-4 "
onClick={onClick}
>
customize
</button>
</div> </div>
); );
}; };
ProductRow.propTypes = { ProductRow.propTypes = {
className: PropTypes.string, className: PropTypes.string,
image17: PropTypes.string.isRequired, image17: PropTypes.string,
shirts: PropTypes.string.isRequired, shirts: PropTypes.string,
price: PropTypes.string.isRequired, // Ensure price is a required prop
onClick: PropTypes.func.isRequired, /** Style props */
propLeft: PropTypes.any, propLeft: PropTypes.any,
propTop: PropTypes.any, propTop: PropTypes.any,
}; };
export default ProductRow; export default ProductRow;

View File

@ -1,67 +0,0 @@
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

@ -1,66 +0,0 @@
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

@ -1,66 +0,0 @@
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

@ -1,17 +0,0 @@
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

@ -1,39 +0,0 @@
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,331 +1,148 @@
import React, { useState, useEffect } from 'react'; import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom'; 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 Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig'; import axiosInstance from '../../api/axiosConfig';
import { createCustomer } from "../../services_1/customersService"; import { useNavigate } from 'react-router-dom';
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 AddCustomer = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const [customerData, setCustomerData] = useState({ const [customerData, setCustomerData] = useState({
firstname: '', firstname: '',
lastname: '', lastname:'',
age: '', age: '',
gender: '', gender: '',
email: '', email: '',
address: '',
mobile: '', 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 handleInputChange = (e) => {
const { name, value } = e.target; const { name, value } = e.target;
setCustomerData({
if (name.startsWith('address.')) { ...customerData,
const addressField = name.split('.')[1]; [name]: value,
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) => { const handleAddCustomerSubmit = async (e) => {
e.preventDefault(); 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 { try {
const data = await createCustomer(customerData); const response = await axiosInstance.post('http://localhost:5000/customers', customerData);
console.log('Customer added successfully:', data);
navigate('/customers'); navigate('/customers');
} catch (error) { } catch (error) {
console.error('Error adding customer:', error.response || error.message); console.error(error);
} }
}; };
return ( return (
<div className={CLASSES.container}> <div className="flex flex-row">
<Sidebar /> <Sidebar />
<div className={CLASSES.mainContent}> <div className="w-full flex flex-col min-h-screen text-white">
<div className={CLASSES.innerContent}> <div className="min-h-screen text-gray-300 p-6">
<div className={CLASSES.header}> <div className="flex justify-between items-center mb-6">
<div className={CLASSES.headerTitle}> <h1 className="text-2xl font-semibold text-#0e355b">add customer</h1>
<Link to="/customers" className={CLASSES.backButton}> <Link to="/" className="text-#0e355b">skip for now</Link>
<FontAwesomeIcon icon={faChevronLeft} />
</Link>
<h1 className={CLASSES.title}>add customer</h1>
</div>
<Link to="/CategoryList" className={CLASSES.skipLink}>
skip for now
</Link>
</div> </div>
<div className={CLASSES.formGrid}> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className={CLASSES.imageSection}> <div className="space-y-6">
<div className={CLASSES.imageWrapper}> <div className="text-center">
<div className={CLASSES.imageContainer}> <div className="inline-block relative">
<div className={CLASSES.imagePreview}> <div className="bg-gray-700 w-24 h-24 rounded-full mx-auto"></div>
{imagePreview ? ( <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">
<img {/* Add Customer Image */}
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> </button>
</div> </div>
</div> </div>
<div> <div>
<Link to="/add-customer"> <Link to="/">
<h2 className={CLASSES.linkTitle}>customer information</h2> <h2 className="text-lg font-semibold text-">customer information</h2>
</Link> </Link>
<Link to="/measurements"> <Link to="/measurements">
<h2 className={CLASSES.linkTitle}>measurements</h2> <h2 className="text-lg font-semibold text-">measurements</h2>
</Link> </Link>
</div> </div>
</div> </div>
<div className={CLASSES.formSection}> <div className="md:col-span-2 space-y-4">
<form onSubmit={handleAddCustomerSubmit} className="space-y-4"> <form onSubmit={handleAddCustomerSubmit}>
<div className={CLASSES.formGroup}> <div>
<label htmlFor="firstname">First Name</label> <label className="block text-d1d5db font-medium text-d1d5db">customer firstname:</label>
<input <input
type="text" type="text"
id="firstname"
name="firstname" name="firstname"
className={CLASSES.formInput}
value={customerData.firstname} value={customerData.firstname}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline"
/> />
</div> </div>
<div className={CLASSES.formGroup}> <div>
<label htmlFor="lastname">Last Name</label> <label className="block text-d1d5db font-medium text-d1d5db">customer lastname:</label>
<input <input
type="text" type="text"
id="lastname"
name="lastname" name="lastname"
className={CLASSES.formInput}
value={customerData.lastname} value={customerData.lastname}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline"
/> />
</div> </div>
<div className={CLASSES.formGroup}> <div>
<label htmlFor="age">Age</label> <label className="block text-d1d5db font-medium text-d1d5db">age:</label>
<input <input
type="number" type="number"
id="age"
name="age" name="age"
className={CLASSES.formInput}
value={customerData.age} value={customerData.age}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline"
/> />
</div> </div>
<div className={CLASSES.formGroup}> <div>
<label htmlFor="gender">Gender</label> <label className="block text-d1d5db font-medium text-d1d5db">gender:</label>
<select <select
id="gender"
name="gender" name="gender"
className={CLASSES.formSelect}
value={customerData.gender} value={customerData.gender}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline"
> >
<option value="">Select gender</option> <option value="" disabled>select gender</option>
<option value="male">Male</option> <option value="male">male</option>
<option value="female">Female</option> <option value="female">female</option>
<option value="other">Other</option> <option value="other">other</option>
</select> </select>
</div> </div>
<div className={CLASSES.formGroup}> <div>
<label htmlFor="email">Email</label> <label className="block text-d1d5db font-medium text-d1d5db">email:</label>
<input <input
type="email" type="email"
id="email"
name="email" name="email"
className={CLASSES.formInput}
value={customerData.email} value={customerData.email}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline"
/> />
</div> </div>
<div className={CLASSES.formGroup}> <div>
<label htmlFor="mobile">Mobile Number</label> <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>
<input <input
type="text" type="text"
id="mobile"
name="mobile" name="mobile"
className={CLASSES.formInput}
value={customerData.mobile} value={customerData.mobile}
onChange={handleInputChange} onChange={handleInputChange}
className="input-underline"
/> />
</div> </div>
<div className={CLASSES.formGroup}> <br />
<label htmlFor="address_line_1">Address Line 1</label> <div className="text-center">
<input <button type="submit" className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md font-semibold text-sm">
type="text" save information
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> </button>
</div> </div>
</form> </form>

View File

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

View File

@ -2,23 +2,21 @@ import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import Sidebar from '../sidebar/Sidebar'; import Sidebar from '../sidebar/Sidebar';
import axiosInstance from '../../api/axiosConfig'; import axiosInstance from '../../api/axiosConfig';
import '../../styles/CustomerList.css';
import {getAllCustomers} from '../../services_1/customersService'
const CustomerList = () => { const CustomerList = () => {
const [customers, setCustomers] = useState([]); const [customers, setCustomers] = useState([]);
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
// Fetch the customers data from the JSON file
getCustomers(); getCustomers();
}, []); }, []);
const getCustomers = async () => { const getCustomers = async () => {
try { try {
const data = await getAllCustomers() const response = await axiosInstance.get('http://localhost:5000/customers');
setCustomers(data); let customers_data = response.data; // List of customers
setCustomers(customers_data);
} catch (error) { } catch (error) {
throw error; throw error;
} }
@ -33,18 +31,18 @@ const CustomerList = () => {
<Sidebar /> <Sidebar />
<div className="flex-grow p-6 bg-gray-100"> <div className="flex-grow p-6 bg-gray-100">
<div className="bg-white p-6 rounded-lg shadow"> <div className="bg-white p-6 rounded-lg shadow">
<div className="flex justify-between items-center "> <div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-semibold text-gray-800">customers list</h1> <h1 className="text-2xl font-semibold text-gray-800">Customers List</h1>
<button <button
className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md text-sm" className="bg-#0e355b text-d1d5db px-4 py-2 rounded-md text-sm"
onClick={handleAddCustomer} onClick={handleAddCustomer}
> >
add new customer Add New Customer
</button> </button>
</div> </div>
<div className="flex items-center "> <div className="flex items-center mb-4">
<svg <svg
className="w-5 h-5 text-gray-500" className="w-5 h-5 mr-2 text-gray-500"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -57,9 +55,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" 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> </svg>
<span className="text-sm text-gray-600">filter</span> <span className="text-sm text-gray-600">Filter</span>
</div> </div>
<p className="text-sm text-gray-600 ">total customers ({customers.length})</p> <p className="text-sm text-gray-600 mb-4">Total Customers ({customers.length})</p>
<table className="w-full bg-white"> <table className="w-full bg-white">
<thead> <thead>
<tr className="text-left text-xs uppercase text-gray-600 border-b"> <tr className="text-left text-xs uppercase text-gray-600 border-b">

View File

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

View File

@ -1,188 +0,0 @@
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

@ -1,142 +0,0 @@
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

@ -1,70 +0,0 @@
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

@ -1,66 +0,0 @@
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

@ -0,0 +1,19 @@
.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

@ -0,0 +1,49 @@
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

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

View File

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

View File

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

View File

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

View File

@ -1,19 +1,12 @@
import React, { useState, useEffect } from 'react'; import React, { useContext } from 'react';
import AdminSidebar from './AdminSidebar'; import AdminSidebar from './AdminSidebar';
import EmployeeSidebar from './EmployeeSidebar'; import EmployeeSidebar from './EmployeeSidebar';
import { AuthContext } from '../../contexts/AuthContext';
const Sidebar = () => { const Sidebar = () => {
const [user, setUserData] = useState(null); const { user } = useContext(AuthContext);
useEffect(() => { if (!user) return null;
// 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 />; return user.role === 'admin' ? <AdminSidebar /> : <EmployeeSidebar />;
}; };

View File

@ -1,61 +0,0 @@
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

@ -1,73 +0,0 @@
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,36 +1,17 @@
const authService = { const authService = {
login: async (username, password) => { login: async (username, password) => {
try { // Simulate login process
// Simulate login process with API call (replace with real API endpoint) const userData = { username, role: username === 'admin' ? 'admin' : 'employee' };
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('user', JSON.stringify(userData));
localStorage.setItem('token', userData.token);
return userData; return userData;
} catch (error) { },
throw new Error('Login failed. Please check your credentials.'); logout: () => {
localStorage.removeItem('user');
},
getCurrentUser: () => {
return JSON.parse(localStorage.getItem('user'));
} }
}, };
logout: () => {
localStorage.removeItem('user'); export default authService;
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;

View File

@ -1,75 +0,0 @@
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}`);
});

View File

@ -1,75 +0,0 @@
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

@ -1,90 +0,0 @@
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

@ -1,87 +0,0 @@
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

@ -1,91 +0,0 @@
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

@ -1,81 +0,0 @@
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

@ -1,102 +0,0 @@
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

@ -1,81 +0,0 @@
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);
}
};

View File

@ -1,148 +0,0 @@
/* 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

@ -1,52 +0,0 @@
/* 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

@ -1,66 +0,0 @@
.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

@ -1,45 +0,0 @@
/* .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,74 +0,0 @@
/* 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 */
}

View File

@ -1,125 +0,0 @@
/* 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

@ -1,66 +0,0 @@
.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

@ -1,62 +0,0 @@
/* .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

@ -1,24 +0,0 @@
.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 */
}

View File

@ -1,107 +0,0 @@
.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;
}

View File

@ -1,20 +0,0 @@
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

@ -1,59 +0,0 @@
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;

View File

@ -1,38 +0,0 @@
/** @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: [],
};

10
tailwind.config.js Normal file
View File

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

View File

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

4012
yarn.lock

File diff suppressed because it is too large Load Diff