Compare commits
No commits in common. "main" and "branch-login" have entirely different histories.
main
...
branch-log
12
.env
12
.env
@ -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
209
db.json
@ -11,12 +11,6 @@
|
||||
"email": "employee@gmail.com",
|
||||
"password": "123",
|
||||
"role": "employee"
|
||||
},
|
||||
{
|
||||
"id": "de4c"
|
||||
},
|
||||
{
|
||||
"id": "5996"
|
||||
}
|
||||
],
|
||||
"health-check": {
|
||||
@ -25,83 +19,29 @@
|
||||
"admin": [
|
||||
{
|
||||
"id": "1",
|
||||
"firstname": "Admin User",
|
||||
"lastname": "last ",
|
||||
"name": "Admin User",
|
||||
"email": "adminuser@example.com"
|
||||
}
|
||||
],
|
||||
"employees": [
|
||||
"employee": [
|
||||
{
|
||||
"id": "1",
|
||||
"firstname": "Employee User",
|
||||
"lastname": "last ",
|
||||
"name": "Employee User",
|
||||
"email": "employeeuser@example.com"
|
||||
},
|
||||
{
|
||||
"id": "6b21",
|
||||
"firstname": "suraj",
|
||||
"lastname": "birewar",
|
||||
"email": "surajbirewar001@gmail.com"
|
||||
},
|
||||
{
|
||||
"id": "d010",
|
||||
"firstname": "gajanan",
|
||||
"lastname": "bodke",
|
||||
"email": "gajanan001@gmail.com"
|
||||
},
|
||||
{
|
||||
"id": "4db0",
|
||||
"firstname": "sachin",
|
||||
"lastname": "patil",
|
||||
"age": "22",
|
||||
"gender": "",
|
||||
"email": "sachin@gmail.com",
|
||||
"mobile": "07559393995",
|
||||
"address": {
|
||||
"address_line_1": "",
|
||||
"address_line_2": "",
|
||||
"nearby_landmark": "",
|
||||
"pincode": "",
|
||||
"city_id": ""
|
||||
},
|
||||
"image": null,
|
||||
"address.address_line_1": "Sarovar line 1 , Bavdhan, Pune",
|
||||
"address.address_line_2": "LOCAL :,Bavdhan",
|
||||
"address.pincode": "411021",
|
||||
"address.nearby_landmark": "i",
|
||||
"address.city_id": "1"
|
||||
}
|
||||
],
|
||||
"products": [
|
||||
{
|
||||
"ID": "1",
|
||||
"name": "Men's T-Shirt",
|
||||
"sku": "TS-MEN-001-BL-XL",
|
||||
"short_description": "A comfortable men's clothing fibers",
|
||||
"long_description": "This men's shirt is made from 100% cotton, providing a soft and comfortable feel. Perfect for casual wear.",
|
||||
"thumbnail_url": "https://example.com/men-tshirt.jpg",
|
||||
"category_id": "101",
|
||||
"id": "d6ce"
|
||||
"id": "1",
|
||||
"name": "Product 1",
|
||||
"category": "Category 1",
|
||||
"price": 100
|
||||
},
|
||||
{
|
||||
"ID": "2",
|
||||
"name": "Women's Jacket",
|
||||
"sku": "JK-WOM-001-RE-M",
|
||||
"short_description": "Stylish women's jacket",
|
||||
"long_description": "A trendy women's jacket, perfect for chilly weather. Made with high-quality materials to ensure warmth and comfort.",
|
||||
"thumbnail_url": "https://example.com/women-jacket.jpg",
|
||||
"category_id": "102",
|
||||
"id": "60db"
|
||||
},
|
||||
{
|
||||
"ID": "3",
|
||||
"name": "Unisex Sneakers",
|
||||
"sku": "SN-UNI-001-WH-42",
|
||||
"short_description": "Comfortable unisex tuxiods",
|
||||
"long_description": "These unisex tuxidos are designed for comfort and style. Ideal for everyday wear.",
|
||||
"thumbnail_url": "https://example.com/tuxidos.jpg",
|
||||
"category_id": "103",
|
||||
"id": "7228"
|
||||
"id": "2",
|
||||
"name": "Product 2",
|
||||
"category": "Category 2",
|
||||
"price": 200
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
@ -117,116 +57,61 @@
|
||||
"customers": [
|
||||
{
|
||||
"id": "1",
|
||||
"firstname": "vitthal",
|
||||
"lastname": "patil",
|
||||
"name": "Customer 1",
|
||||
"email": "customer1@example.com",
|
||||
"phone": "1234567890"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"firstname": "rushi",
|
||||
"lastname": "shelke",
|
||||
"name": "Customer 2",
|
||||
"email": "customer2@example.com",
|
||||
"phone": "0987654321"
|
||||
},
|
||||
{
|
||||
"id": "7779",
|
||||
"firstname": "Suraj",
|
||||
"lastname": "Birewar",
|
||||
"age": "22",
|
||||
"gender": "male",
|
||||
"name": "Suraj Birewar",
|
||||
"age": "24",
|
||||
"gender": "Male",
|
||||
"email": "surajbirewar001@gmail.com",
|
||||
"address": "Bavdhan, Pune",
|
||||
"mobile": "07559393995",
|
||||
"address": {
|
||||
"address_line_1": "Bavdhan, Pune",
|
||||
"address_line_2": "LOCAL :,Bavdhan",
|
||||
"nearby_landmark": "sbi",
|
||||
"pincode": "411021",
|
||||
"city_id": "1"
|
||||
},
|
||||
"image": null
|
||||
}
|
||||
],
|
||||
"cities": [
|
||||
{
|
||||
"id": "1",
|
||||
"name": "New York"
|
||||
"id": "3"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "Los Angeles"
|
||||
"name": "rahul",
|
||||
"age": "21",
|
||||
"gender": "male",
|
||||
"email": "rahul001@gmail.com",
|
||||
"address": "Bavdhan, Pune",
|
||||
"mobile": "7559393995",
|
||||
"id": "4"
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "Chicago"
|
||||
"name": "atharv",
|
||||
"age": "19",
|
||||
"gender": "male",
|
||||
"email": "atharv@gmail.com",
|
||||
"address": "baner pune ",
|
||||
"mobile": "784365873",
|
||||
"id": "5"
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
"name": "Houston"
|
||||
"id": "3682",
|
||||
"name": "dikshant",
|
||||
"age": "23",
|
||||
"gender": "male",
|
||||
"email": "dikshant001@gmail.com",
|
||||
"address": "Bavdhan, Pune",
|
||||
"mobile": "45654665645"
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
"name": "Phoenix"
|
||||
},
|
||||
{
|
||||
"id": "333e",
|
||||
"name": "las vegas"
|
||||
}
|
||||
],
|
||||
"users": [
|
||||
{
|
||||
"ID": "1",
|
||||
"Email": "adminuser@example.com",
|
||||
"Roles": [
|
||||
"admin"
|
||||
],
|
||||
"Profile": {
|
||||
"ID": "p1",
|
||||
"FirstName": "Admin",
|
||||
"LastName": "User",
|
||||
"Phone": "1234567890",
|
||||
"Age": 35,
|
||||
"Gender": "MALE"
|
||||
},
|
||||
"CreatedAt": "2023-01-01T10:00:00Z",
|
||||
"UpdatedAt": "2023-01-01T12:00:00Z",
|
||||
"id": "2506"
|
||||
},
|
||||
{
|
||||
"ID": "2",
|
||||
"Email": "employeeuser@example.com",
|
||||
"Roles": [
|
||||
"employee"
|
||||
],
|
||||
"Profile": {
|
||||
"ID": "p2",
|
||||
"FirstName": "Employee",
|
||||
"LastName": "User",
|
||||
"Phone": "0987654321",
|
||||
"Age": 28,
|
||||
"Gender": "MALE"
|
||||
},
|
||||
"CreatedAt": "2023-01-02T10:00:00Z",
|
||||
"UpdatedAt": "2023-01-02T12:00:00Z",
|
||||
"id": "bd52"
|
||||
},
|
||||
{
|
||||
"ID": "3",
|
||||
"Email": "newuser@example.com",
|
||||
"Roles": [
|
||||
"customer"
|
||||
],
|
||||
"Profile": {
|
||||
"ID": "p3",
|
||||
"FirstName": "New",
|
||||
"LastName": "User",
|
||||
"Phone": "1122334455",
|
||||
"Age": 22,
|
||||
"Gender": "FEMALE"
|
||||
},
|
||||
"CreatedAt": "2023-01-03T10:00:00Z",
|
||||
"UpdatedAt": "2023-01-03T12:00:00Z",
|
||||
"id": "eefd"
|
||||
"id": "377d",
|
||||
"firstname": "tejas",
|
||||
"lastname": "chari",
|
||||
"age": "28",
|
||||
"gender": "male",
|
||||
"email": "tejas001@gmail.com",
|
||||
"address": " Baner, Pune",
|
||||
"mobile": "8675493099"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
import axios from 'axios';
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL: 'http://localhost:5000',
|
||||
timeout: 10000,
|
||||
baseURL: 'http://localhost:5000'
|
||||
});
|
||||
|
||||
export default axiosInstance;
|
18164
package-lock.json
generated
18164
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
31
package.json
31
package.json
@ -16,45 +16,24 @@
|
||||
"proxy": "http://localhost:5000",
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.3.7",
|
||||
"@craco/craco": "^7.1.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.6.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.6.0",
|
||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||
"@pankod/refine-simple-rest": "^3.39.0",
|
||||
"@refinedev/core": "^4.53.0",
|
||||
"@refinedev/kbar": "^1.3.12",
|
||||
"@refinedev/mui": "^5.19.0",
|
||||
"@refinedev/simple-rest": "^5.0.8",
|
||||
"antd": "^5.18.2",
|
||||
"axios": "^1.7.2",
|
||||
"classnames": "^2.5.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.25.1",
|
||||
"react-toastify": "^10.0.5"
|
||||
"react-router-dom": "^6.23.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^6.4.8",
|
||||
"@testing-library/react": "^16.0.0",
|
||||
"@types/react": "^18.2.66",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^9.8.8",
|
||||
"axios-mock-adapter": "^1.22.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"jest": "^27.5.1",
|
||||
"json-server": "^1.0.0-beta.1",
|
||||
"postcss": "^7.0.39",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
|
||||
"vite": "^5.3.4"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"moduleNameMapper": {
|
||||
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
|
||||
}
|
||||
"postcss": "^8.4.38",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"vite": "^5.2.0"
|
||||
}
|
||||
}
|
||||
|
85
src/App.jsx
85
src/App.jsx
@ -1,81 +1,44 @@
|
||||
import React from "react";
|
||||
import { BrowserRouter as Router, Route, Routes, Navigate } from "react-router-dom";
|
||||
import { Layout } from "antd";
|
||||
import LoginPage from "./components/Auth/LoginPage";
|
||||
import ResetPasswordRequest from "./components/Auth/ResetPasswordRequest";
|
||||
import AdminDashboard from "./pages/AdminDashboard";
|
||||
import EmployeeDashboard from "./pages/EmployeeDashboard";
|
||||
import CustomerList from "./components/customer/CustomerList";
|
||||
import AddCustomer from "./components/customer/AddCustomer";
|
||||
import Sidebar from "./components/sidebar/Sidebar";
|
||||
import CustomerMeasurements from "./components/customer/CustomerMeasurements";
|
||||
import { AuthProvider, AuthContext } from "./contexts/AuthContext";
|
||||
import EmployeeList from "./components/admin/EmployeeList";
|
||||
import AddEmployee from "./components/admin/AddEmployee";
|
||||
import EmployeeProfile from "./components/admin/EmployeeProfile";
|
||||
import CategoryList from "./components/categories/CategoryList";
|
||||
import ShirtsProductList from "./components/categories/ShirtsProductList";
|
||||
import SuitsProductList from "./components/categories/SuitsProductList";
|
||||
import TuxedosProductList from "./components/categories/TuxedosProductList";
|
||||
|
||||
|
||||
import JacketsProductList from "./components/categories/JacketsProductList.jsx";
|
||||
import Customize from "./components/customize/Customize.jsx";
|
||||
import SetMeasurements from "./components/customize/SetMeasurements.jsx";
|
||||
import StandardMeasurements from "./components/customize/StandardMeasurements.jsx";
|
||||
import CustomMeasurements from "./components/customize/CustomMeasurements.jsx";
|
||||
|
||||
|
||||
|
||||
import { Refine } from "@refinedev/core";
|
||||
import { RefineKbarProvider } from "@refinedev/kbar";
|
||||
import { ToastContainer, toast } from "react-toastify";
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
|
||||
import React, { useContext } from 'react';
|
||||
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom';
|
||||
import { Layout } from 'antd';
|
||||
import LoginPage from './components/Auth/LoginPage';
|
||||
import AdminDashboard from './components/admin/AdminDashboard';
|
||||
import EmployeeDashboard from './components/employee/EmployeeDashboard';
|
||||
import CustomerList from './components/customer/CustomerList';
|
||||
import AddCustomer from './components/customer/AddCustomer';
|
||||
import Sidebar from './components/sidebar/Sidebar';
|
||||
import CustomerMeasurements from './components/customer/CustomerMeasurements';
|
||||
import { AuthProvider, AuthContext } from './contexts/AuthContext';
|
||||
import EmployeeList from './components/admin/EmployeeList';
|
||||
import AddEmployee from './components/admin/AddEmployee';
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
const App = () => {
|
||||
const { user } = { name: "Suraj" }; // useContext(AuthContext);
|
||||
const { user } = { name: 'Suraj' }; // useContext(AuthContext);
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<RefineKbarProvider>
|
||||
<Refine>
|
||||
<div className="min-h-screen flex w-full">
|
||||
<Content className="flex-1 text-white">
|
||||
<AuthProvider>
|
||||
<div className="min-h-screen flex w-full">
|
||||
<Content className="flex-1 text-white">
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route exact path="/" element={<LoginPage />} />
|
||||
<Route path="/reset-password" element={<ResetPasswordRequest />} />
|
||||
<Route path="/employee/home" element={<EmployeeDashboard />} />
|
||||
<Route path="/employee/home" element={<EmployeeDashboard />} /> {/* Add HomePage route */}
|
||||
<Route path="/admin" element={<PrivateRoute><AdminDashboard /></PrivateRoute>} />
|
||||
<Route path="/employee" element={<PrivateRoute><EmployeeDashboard /></PrivateRoute>} />
|
||||
<Route path="/customers" element={<PrivateRoute><CustomerList /></PrivateRoute>} />
|
||||
<Route path="/add-customer" element={<PrivateRoute><AddCustomer /></PrivateRoute>} />
|
||||
<Route path="/employee/create-order" element={<CustomerList />} />
|
||||
<Route path="/measurements" element={<CustomerMeasurements />} />
|
||||
<Route path="/employees" element={<PrivateRoute><EmployeeList /></PrivateRoute>} />
|
||||
<Route path="employees" element={<PrivateRoute><EmployeeList /></PrivateRoute>} />
|
||||
<Route path="/add-employee" element={<PrivateRoute><AddEmployee /></PrivateRoute>} />
|
||||
<Route path="/employee-profile" element={<PrivateRoute><EmployeeProfile /></PrivateRoute>} />
|
||||
<Route path="/CategoryList" element={<PrivateRoute><CategoryList /></PrivateRoute>} />
|
||||
<Route path="/products/shirts" element={<ShirtsProductList />} />
|
||||
<Route path="/products/suits" element={<SuitsProductList />} />
|
||||
<Route path="/products/tuxedos" element={<TuxedosProductList />} />
|
||||
<Route path="/products/jackets" element={<JacketsProductList />} />
|
||||
<Route path="/customize/:id" element={<Customize />} />
|
||||
<Route path="/setmeasurements" element={<SetMeasurements />} />
|
||||
<Route path="/measurements/standard" element={<StandardMeasurements />} />
|
||||
<Route path="/measurements/custom" element={<CustomMeasurements />} />
|
||||
<Route path="/orders" element={<CustomMeasurements />} />
|
||||
</Routes>
|
||||
</Content>
|
||||
</div>
|
||||
</AuthProvider>
|
||||
</Refine>
|
||||
</RefineKbarProvider>
|
||||
<ToastContainer /> {/* Added ToastContainer for displaying notifications */}
|
||||
</Router>
|
||||
</Router>
|
||||
</AuthProvider>
|
||||
</Content>
|
||||
</div>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,49 +1,20 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// Determine environment and set baseURL accordingly
|
||||
const environment = import.meta.env.VITE_ENVIRONMENT;
|
||||
const baseURL = environment === 'production'
|
||||
? import.meta.env.VITE_API_URL
|
||||
: 'http://localhost:5000'; // Default for development
|
||||
|
||||
console.log(`Axios baseURL set to: ${baseURL}`); // Log the base URL for debugging
|
||||
|
||||
const axiosInstance = axios.create({
|
||||
baseURL,
|
||||
baseURL: '/api',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
// Request interceptor to add JWT token
|
||||
axiosInstance.interceptors.request.use((config) => {
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
config.headers['Authorization'] = `Bearer ${token}`;
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
}, error => {
|
||||
// Handle request error
|
||||
console.error('Request error:', error);
|
||||
}, (error) => {
|
||||
return Promise.reject(error);
|
||||
});
|
||||
|
||||
// Response interceptor to handle responses globally
|
||||
axiosInstance.interceptors.response.use(
|
||||
response => response,
|
||||
error => {
|
||||
if (error.response) {
|
||||
// Server responded with a status other than 2xx
|
||||
console.error('Error response:', error.response.data);
|
||||
} else if (error.request) {
|
||||
// No response received from the server
|
||||
console.error('Error request:', error.request);
|
||||
} else {
|
||||
// Something else caused the error
|
||||
console.error('Error message:', error.message);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default axiosInstance;
|
||||
|
@ -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}`);
|
||||
};
|
@ -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();
|
||||
}
|
||||
});
|
||||
});
|
@ -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;
|
||||
}
|
||||
};
|
@ -5,6 +5,7 @@ body, html {
|
||||
padding: 0;
|
||||
font-family: 'Nunito Sans', sans-serif;
|
||||
background-color: #ffffff;
|
||||
/* color: #fff; */
|
||||
}
|
||||
|
||||
.login-page {
|
||||
@ -14,15 +15,28 @@ body, html {
|
||||
min-height: 100vh;
|
||||
padding: 4rem;
|
||||
background-color: #ffffff;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.logo {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 1rem;
|
||||
width: 100px;
|
||||
height: auto;
|
||||
.login-container {
|
||||
/* background-color: #222; */
|
||||
border-radius: 8px;
|
||||
padding: 40px;
|
||||
box-shadow: 0 0 10px #d1d5db;
|
||||
width: 310.4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 28px;
|
||||
column-gap: 28px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@ -37,20 +51,44 @@ p {
|
||||
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%;
|
||||
padding: 12px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #B3B3B3;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
background-color: #e8e8e8;
|
||||
color: #4b5563;
|
||||
background-color: #09090B;
|
||||
color: #fff;
|
||||
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 {
|
||||
border-color: #0e355b;
|
||||
input[type="email"]:focus,
|
||||
input[type="password"]:focus {
|
||||
border-color: #4b5563;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@ -67,6 +105,10 @@ input:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* .forgot-password-container a:hover {
|
||||
text-decoration: underline;
|
||||
} */
|
||||
|
||||
.login-button {
|
||||
width: 100%;
|
||||
padding: 15px 30px;
|
||||
@ -81,3 +123,15 @@ input:focus {
|
||||
.login-button:hover {
|
||||
background-color: #154676;
|
||||
}
|
||||
|
||||
.login-page input:focus {
|
||||
border-color: #0e355b; /* Orange border on focus */
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.login-page a {
|
||||
color: #4b5563;
|
||||
text-decoration: none;
|
||||
margin-bottom: 20px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import axiosInstance from '../../api/axiosConfig';
|
||||
import BrookslogoIcon from '../../assets/thob-data/BrookslogoIcon.svg';
|
||||
import './LoginPage.css';
|
||||
|
||||
const LoginPage = () => {
|
||||
const [email, setEmail] = useState('');
|
||||
@ -15,16 +15,16 @@ const LoginPage = () => {
|
||||
|
||||
try {
|
||||
const response = await axiosInstance.get('http://localhost:5000/authlogin');
|
||||
|
||||
const user = response.data.find(user => user.email === email && user.password === password);
|
||||
|
||||
if (user) {
|
||||
localStorage.setItem("loggedInUser", JSON.stringify(user));
|
||||
if (user.role === 'admin') {
|
||||
navigate('/admin');
|
||||
} else if (user.role === 'employee') {
|
||||
navigate('/employee');
|
||||
} else {
|
||||
setError('Invalid role.');
|
||||
setError('Invalid credentials');
|
||||
}
|
||||
} else {
|
||||
setError('Invalid credentials');
|
||||
@ -41,42 +41,42 @@ const LoginPage = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen bg-secondary p-4 relative">
|
||||
<img src={BrookslogoIcon} alt="Logo" className="absolute top-6 left-6 w-24" />
|
||||
<div className="flex flex-col items-center justify-center flex-1 px-4 py-6">
|
||||
<h1 className="text-primary-text text-2xl font-bold mb-4">welcome back</h1>
|
||||
<p className="text-secondary-text text-center mb-6">be among the first to experience 3D magic! Register for private alpha.</p>
|
||||
{error && <p className="text-red-500 text-center mb-4">{error}</p>}
|
||||
<div className="flex flex-col space-y-4 w-full max-w-sm">
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="john123@domain.com"
|
||||
required
|
||||
className="w-full p-3 border border-gray-300 rounded-md text-gray-700 focus:border-primary focus:outline-none"
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Enter your password"
|
||||
required
|
||||
className="w-full p-3 border border-gray-300 rounded-md text-gray-700 focus:border-primary focus:outline-none"
|
||||
/>
|
||||
<div className="flex justify-end">
|
||||
<a href="/reset-password" className="text-primary-text">forgot password?</a>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleLogin}
|
||||
className="w-full py-3 bg-primary text-button-text rounded-md text-lg font-semibold hover:bg-primary-dark"
|
||||
>
|
||||
login
|
||||
</button>
|
||||
<div className="flex flex-row min-h-screen justify-center items-center">
|
||||
<div className="login-page flex items-center justify-center min-h-screen w-full md:w-1/2 lg:w-1/3 p-4">
|
||||
<div className="w-full">
|
||||
<h1>Welcome Back</h1>
|
||||
<p>Be among the first to experience 3D magic! Register for private alpha.</p>
|
||||
{error && <p className="error-message">{error}</p>}
|
||||
<form onSubmit={handleLogin} className="space-y-4">
|
||||
<label htmlFor="email" className="block"></label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="john123@domain.com"
|
||||
required
|
||||
className="input-field"
|
||||
/>
|
||||
<label htmlFor="password" className="block"></label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Enter your password"
|
||||
required
|
||||
className="input-field"
|
||||
/>
|
||||
<div className="forgot-password-container">
|
||||
<a href="#">Forgot password?</a>
|
||||
</div>
|
||||
<button type="submit" className="login-button">
|
||||
Login
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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;
|
@ -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;
|
@ -1,87 +1,29 @@
|
||||
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 [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
const [newUser, setNewUser] = useState({ email: '', roles: '' });
|
||||
|
||||
useEffect(() => {
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
const userData = await getUsers();
|
||||
setUsers(userData);
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchUsers();
|
||||
axiosInstance.get('/users')
|
||||
.then(response => {
|
||||
setUsers(response.data);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('There was an error fetching the users!', error);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleCreateUser = async () => {
|
||||
try {
|
||||
const createdUser = await createUser(newUser);
|
||||
setUsers([...users, createdUser]);
|
||||
setNewUser({ email: '', roles: '' });
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateUser = async (userId, updatedData) => {
|
||||
try {
|
||||
const updatedUser = await updateUser(userId, updatedData);
|
||||
setUsers(users.map((user) => (user.ID === userId ? updatedUser : user)));
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteUser = async (userId) => {
|
||||
try {
|
||||
await deleteUser(userId);
|
||||
setUsers(users.filter((user) => user.ID !== userId));
|
||||
} catch (error) {
|
||||
setError(error);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) return <p>Loading...</p>;
|
||||
if (error) return <p>Error loading users: {error.message}</p>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>User List</h2>
|
||||
<h1>Users List</h1>
|
||||
<ul>
|
||||
{users.map((user) => (
|
||||
<li key={user.ID}>
|
||||
{user.Email} - {user.Roles.join(', ')}
|
||||
<button onClick={() => handleUpdateUser(user.ID, {/* Updated data */})}>Update</button>
|
||||
<button onClick={() => handleDeleteUser(user.ID)}>Delete</button>
|
||||
</li>
|
||||
{users.map(user => (
|
||||
<li key={user.id}>{user.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<h3>Add New User</h3>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
value={newUser.email}
|
||||
onChange={(e) => setNewUser({ ...newUser, email: e.target.value })}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Roles (comma-separated)"
|
||||
value={newUser.roles}
|
||||
onChange={(e) => setNewUser({ ...newUser, roles: e.target.value.split(',') })}
|
||||
/>
|
||||
<button onClick={handleCreateUser}>Create User</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserList;
|
||||
export default UsersList;
|
||||
|
@ -1,33 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import Sidebar from '../sidebar/Sidebar';
|
||||
import axiosInstance from '../../api/axiosConfig';
|
||||
import { createEmployee } from "../../services_1/employeeService";
|
||||
|
||||
const CLASSES = {
|
||||
container: 'flex flex-row',
|
||||
mainContent: 'w-full flex flex-col min-h-screen text-black',
|
||||
innerContent: 'min-h-screen text-gray-800 p-6',
|
||||
header: 'flex justify-between items-center',
|
||||
headerTitle: 'flex items-center',
|
||||
backButton: 'text-[#0e355b] text-xl',
|
||||
title: 'text-2xl font-semibold text-[#0e355b]',
|
||||
formGrid: 'grid grid-cols-1 md:grid-cols-3 gap-6',
|
||||
imageSection: 'space-y-6',
|
||||
imageWrapper: 'text-center',
|
||||
imageContainer: 'inline-block relative',
|
||||
imagePreview: 'bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden',
|
||||
noImage: 'w-full h-full flex items-center justify-center text-gray-500',
|
||||
hiddenInput: 'hidden',
|
||||
uploadButton: 'absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-gray-700 text-white text-xs px-2 py-1 rounded-full',
|
||||
formSection: 'md:col-span-2 space-y-4',
|
||||
formGroup: 'text-gray-800 font-medium',
|
||||
formInput: 'p-2 border-b border-gray-400 focus:border-gray-700',
|
||||
formSelect: 'p-2 border-b border-gray-400 focus:border-gray-700',
|
||||
submitButton: 'bg-[#0e355b] text-white py-2 px-4 rounded-md',
|
||||
};
|
||||
|
||||
const AddEmployee = () => {
|
||||
const navigate = useNavigate();
|
||||
@ -37,65 +11,22 @@ const AddEmployee = () => {
|
||||
age: '',
|
||||
gender: '',
|
||||
email: '',
|
||||
address: '',
|
||||
mobile: '',
|
||||
address: {
|
||||
address_line_1: '',
|
||||
address_line_2: '',
|
||||
nearby_landmark: '',
|
||||
pincode: '',
|
||||
city_id: '',
|
||||
},
|
||||
image: null,
|
||||
});
|
||||
const [cities, setCities] = useState([]);
|
||||
const [imagePreview, setImagePreview] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCities = async () => {
|
||||
try {
|
||||
const response = await axiosInstance.get('/cities');
|
||||
if (Array.isArray(response.data)) {
|
||||
setCities(response.data);
|
||||
} else {
|
||||
console.error('Error: Data is not an array', response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching cities:', error.response ? error.response.data : error.message);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCities();
|
||||
}, []);
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setEmployeeData((prevData) => ({
|
||||
...prevData,
|
||||
setEmployeeData({
|
||||
...employeeData,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleImageChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
setEmployeeData({
|
||||
...employeeData,
|
||||
image: file,
|
||||
});
|
||||
setImagePreview(URL.createObjectURL(file));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleAddEmployeeSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData();
|
||||
Object.keys(employeeData).forEach((key) => {
|
||||
formData.append(key, employeeData[key]);
|
||||
});
|
||||
|
||||
try {
|
||||
const data = await createEmployee(employeeData);
|
||||
console.log('Employee added successfully:', data);
|
||||
await axiosInstance.post('/employees', employeeData);
|
||||
navigate('/employees');
|
||||
} catch (error) {
|
||||
console.error('Error adding employee:', error.response || error.message);
|
||||
@ -103,170 +34,107 @@ const AddEmployee = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={CLASSES.container}>
|
||||
<div className="flex flex-row">
|
||||
<Sidebar />
|
||||
<div className={CLASSES.mainContent}>
|
||||
<div className={CLASSES.innerContent}>
|
||||
<div className={CLASSES.header}>
|
||||
<div className={CLASSES.headerTitle}>
|
||||
<Link to="/employees" className={CLASSES.backButton}>
|
||||
<FontAwesomeIcon icon={faChevronLeft} />
|
||||
</Link>
|
||||
<h1 className={CLASSES.title}>create employee account</h1>
|
||||
</div>
|
||||
<div className="w-full flex flex-col min-h-screen text-black">
|
||||
<div className="min-h-screen text-gray-800 p-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h1 className="text-2xl font-semibold text-blue-900">Creating Employee Account</h1>
|
||||
<Link to="/" className="text-blue-900">Skip for now</Link>
|
||||
</div>
|
||||
<div className={CLASSES.formGrid}>
|
||||
<div className={CLASSES.imageSection}>
|
||||
<div className={CLASSES.imageWrapper}>
|
||||
<div className={CLASSES.imageContainer}>
|
||||
<div className={CLASSES.imagePreview}>
|
||||
{imagePreview ? (
|
||||
<img
|
||||
src={imagePreview}
|
||||
alt="Employee"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className={CLASSES.noImage}>no image</div>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className={CLASSES.hiddenInput}
|
||||
id="imageUpload"
|
||||
onChange={handleImageChange}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => document.getElementById('imageUpload').click()}
|
||||
className={CLASSES.uploadButton}
|
||||
>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<div className="inline-block relative">
|
||||
<div className="bg-gray-300 w-24 h-24 rounded-full mx-auto"></div>
|
||||
<button className="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-#4b5563 text-white text-xs px-2 py-1 rounded-full">
|
||||
Add Employee Image
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={CLASSES.formSection}>
|
||||
<div className="md:col-span-2 space-y-4">
|
||||
<form onSubmit={handleAddEmployeeSubmit}>
|
||||
<div>
|
||||
<label className={CLASSES.formGroup}>first name:</label>
|
||||
<label className="block text-gray-800 font-medium">Employee Firstname:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="firstname"
|
||||
value={employeeData.firstname}
|
||||
onChange={handleInputChange}
|
||||
className={CLASSES.formInput}
|
||||
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className={CLASSES.formGroup}>last name:</label>
|
||||
<label className="block text-gray-800 font-medium">Employee Lastname:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="lastname"
|
||||
value={employeeData.lastname}
|
||||
onChange={handleInputChange}
|
||||
className={CLASSES.formInput}
|
||||
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className={CLASSES.formGroup}>age:</label>
|
||||
<label className="block text-gray-800 font-medium">Age:</label>
|
||||
<input
|
||||
type="number"
|
||||
name="age"
|
||||
value={employeeData.age}
|
||||
onChange={handleInputChange}
|
||||
className={CLASSES.formInput}
|
||||
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={CLASSES.formGroup}>
|
||||
<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>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">Gender:</label>
|
||||
<select
|
||||
id="city_id"
|
||||
name="address.city_id"
|
||||
className={CLASSES.formSelect}
|
||||
value={employeeData.address.city_id}
|
||||
name="gender"
|
||||
value={employeeData.gender}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline w-full p-2 border-b border-gray-400 focus:border-#4b5563"
|
||||
>
|
||||
<option value="">Select City</option>
|
||||
{cities.map((city) => (
|
||||
<option key={city.id} value={city.id}>
|
||||
{city.name}
|
||||
</option>
|
||||
))}
|
||||
<option value="" disabled>Select Gender</option>
|
||||
<option value="male">Male</option>
|
||||
<option value="female">Female</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" className={CLASSES.submitButton}>
|
||||
add employee
|
||||
</button>
|
||||
<div>
|
||||
<label className="block text-gray-800 font-medium">Email:</label>
|
||||
<input
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
|
53
src/components/admin/AdminDashboard.jsx
Normal file
53
src/components/admin/AdminDashboard.jsx
Normal 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;
|
89
src/components/admin/Dashboard.css
Normal file
89
src/components/admin/Dashboard.css
Normal 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%;
|
||||
}
|
||||
}
|
@ -2,33 +2,35 @@ import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Sidebar from '../sidebar/Sidebar';
|
||||
import axiosInstance from '../../api/axiosConfig';
|
||||
|
||||
// import EmployeeCards from './EmployeeCards';
|
||||
// import PropTypes from 'prop-types';
|
||||
|
||||
const EmployeeList = ({ className = '' }) => {
|
||||
const [employees, setEmployees] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch the employees data from the JSON file
|
||||
getEmployees();
|
||||
}, []);
|
||||
|
||||
const getEmployees = async () => {
|
||||
try {
|
||||
const response = await axiosInstance.get('http://localhost:5000/employees');
|
||||
let employees_data = response.data;
|
||||
let employees_data = response.data; // List of employees
|
||||
setEmployees(employees_data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching employees:', error.response || error.message);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddEmployee = () => {
|
||||
navigate('/add-employee');
|
||||
};
|
||||
navigate('/add-employee');
|
||||
};
|
||||
|
||||
const onEmployeeClick = useCallback(() => {
|
||||
navigate('/employee-profile');
|
||||
}, [navigate]);
|
||||
// const onEmployeeCardsClick = useCallback(() => {
|
||||
// navigate('/employee-profile');
|
||||
// }, [navigate]);
|
||||
|
||||
return (
|
||||
<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="bg-white p-6 rounded-lg shadow">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h1 className="text-2xl font-semibold text-gray-800">employees list</h1>
|
||||
<h1 className="text-2xl font-semibold text-gray-800">Employees List</h1>
|
||||
<button
|
||||
className="bg-#0e355b text-d1d5db px-4 py-2 rounded-md text-sm"
|
||||
onClick={handleAddEmployee}
|
||||
>
|
||||
add new employee
|
||||
Add New Employee
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center mb-4">
|
||||
@ -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"
|
||||
/>
|
||||
</svg>
|
||||
<span className="text-sm text-gray-600">filter</span>
|
||||
<span className="text-sm text-gray-600">Filter</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 mb-4">total employees: ({employees.length})</p>
|
||||
<p className="text-sm text-gray-600 mb-4">Total Employees ({employees.length})</p>
|
||||
<table className="w-full bg-white">
|
||||
<thead>
|
||||
<tr className="text-left text-xs uppercase text-gray-600 border-b">
|
||||
@ -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.email}</td>
|
||||
<td className="py-2 text-gray-800">
|
||||
<span
|
||||
className="text-#4b5563 underline cursor-pointer hover:text-#0e355b transition"
|
||||
onClick={onEmployeeClick}
|
||||
<button
|
||||
className="text-#4b5563 underline"
|
||||
// onClick={onEmployeeCardsClick}
|
||||
>
|
||||
view details
|
||||
</span>
|
||||
View Details
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
@ -95,4 +97,8 @@ const EmployeeList = ({ className = '' }) => {
|
||||
);
|
||||
};
|
||||
|
||||
// EmployeeList.propTypes = {
|
||||
// className: PropTypes.string,
|
||||
// };
|
||||
|
||||
export default EmployeeList;
|
||||
|
@ -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;
|
@ -1,51 +1,124 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Sidebar from '../sidebar/Sidebar';
|
||||
import '../../styles/ProductList.css';
|
||||
import ProductRow from "./ProductRow";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const CategoryList = () => {
|
||||
const CategoryList = ({ className = "" }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Updated categories array with names and specifications
|
||||
const categories = [
|
||||
{ name: 'Shirts', image: 'https://brooksbingham.com/51-large_default/light-blue-jetstream.jpg', description: 'shirts' },
|
||||
{ name: 'Suits', image: 'https://img.freepik.com/free-psd/realistic-suit-illustration_23-2151236757.jpg?t=st=1722512180~exp=1722515780~hmac=79030884ba3866ce8461b252477505e74e625c63012705c110eced45b59c63fb&w=740', description: 'suits' },
|
||||
{ name: 'Tuxedos', image: 'https://img.freepik.com/premium-vector/businessman-suit_98292-5674.jpg?w=740', description: 'tuxedos' },
|
||||
{ name: 'Jackets', image: 'https://i5.walmartimages.com/seo/Lmtime-Clearance-Men-s-Casual-Blazer-Suit-Jackets-Slim-Fit-Lightweight-Sport-Coat-Business-Coat-Suit-Tops-Jacket-One-Button-Grey-XXL_4277f765-9e49-4ed1-a4b1-c70c71d3d1f0.5c005fe2c8e9d110bfb00216fb52f77b.jpeg?odnHeight=768&odnWidth=768&odnBg=FFFFFF', description: 'jackets' },
|
||||
];
|
||||
|
||||
const handleClick = (category) => {
|
||||
navigate(`/products/${category}`);
|
||||
const handleNavigation = (path) => {
|
||||
navigate(path);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
{/* Sidebar */}
|
||||
<Sidebar />
|
||||
|
||||
{/* Main content */}
|
||||
<div className="flex-grow flex flex-col">
|
||||
<div className="bg-white p-4 flex items-center">
|
||||
<h1 className="ml-4 text-2xl font-medium text-gray-700">Categories</h1>
|
||||
</div>
|
||||
<div className="flex-grow p-8">
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
{categories.map((category, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="border border-gray-300 p-4 rounded-lg cursor-pointer hover:shadow-lg transition-shadow duration-300 ease-in-out flex flex-col items-center"
|
||||
onClick={() => handleClick(category.name.toLowerCase())} // Updated to handle lowercase category names
|
||||
>
|
||||
<img src={category.image} alt={category.name} className="w-48 h-48 object-cover" />
|
||||
<h2 className="text-xl font-bold text-center mt-2 capitalize">{category.name}</h2>
|
||||
<p className="ml-4 text-2xl font-medium text-gray-700">{category.description}</p>
|
||||
<p className="text-center text-sm text-gray-600 mt-1">Click to view products</p>
|
||||
<div
|
||||
className={`w-full bg-primary overflow-y-auto flex flex-row items-start justify-start gap-[3px] leading-[normal] tracking-[normal] text-center text-29xl text-primary1 font-button mq1100:flex-wrap ${className}`}
|
||||
>
|
||||
<div className="flex flex-col items-start justify-start py-0 pr-[33px] pl-0">
|
||||
<div className="overflow-hidden flex flex-col items-start justify-start pt-4 px-5 pb-[50px] gap-[235.5px] mq450:pb-[21px] mq450:box-border mq1025:pt-5 mq1025:pb-8 mq1025:box-border">
|
||||
<div className="flex flex-col items-start justify-start gap-[146.5px]">
|
||||
<div className="w-10 h-[147px] relative">
|
||||
<img
|
||||
className="absolute h-[27.21%] w-full top-[0%] right-[0%] bottom-[72.79%] left-[0%] max-w-full overflow-hidden max-h-full"
|
||||
loading="lazy"
|
||||
alt=""
|
||||
src="/brooks-logo.svg"
|
||||
/>
|
||||
<div className="absolute top-[70px] left-[calc(50%_-_20px)] w-10 h-[77px] hidden">
|
||||
<div className="absolute top-[0px] left-[0px] w-full h-full hidden">
|
||||
<div className="absolute top-[0px] left-[0px] rounded-[50px] box-border w-full h-full border-[1px] border-solid border-secondary-accent" />
|
||||
<div className="absolute top-[40px] left-[3px] rounded-[50%] bg-secondary-accent w-[34px] h-[34px]" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-row items-start justify-start py-0 pr-2 pl-[7px]">
|
||||
<div className="flex flex-col items-start justify-start gap-[70px]">
|
||||
<img
|
||||
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
|
||||
loading="lazy"
|
||||
alt="Home"
|
||||
src="/home-icon.svg"
|
||||
onClick={() => handleNavigation('/home')}
|
||||
/>
|
||||
<img
|
||||
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
|
||||
loading="lazy"
|
||||
alt="Categories"
|
||||
src="/category-icon.svg"
|
||||
onClick={() => handleNavigation('/categories')}
|
||||
/>
|
||||
<img
|
||||
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
|
||||
loading="lazy"
|
||||
alt="Customer List"
|
||||
src="/customer-list-icon.svg"
|
||||
onClick={() => handleNavigation('/customers')}
|
||||
/>
|
||||
<img
|
||||
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
|
||||
loading="lazy"
|
||||
alt="Orders"
|
||||
src="/orders-icon.svg"
|
||||
onClick={() => handleNavigation('/employee/create-order')}
|
||||
/>
|
||||
<img
|
||||
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
|
||||
loading="lazy"
|
||||
alt="Account"
|
||||
src="/account-icon.svg"
|
||||
onClick={() => handleNavigation('/account')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-row items-start justify-start py-0 px-2">
|
||||
<div className="flex flex-col items-start justify-start">
|
||||
<img
|
||||
className="w-6 h-6 relative overflow-hidden shrink-0 cursor-pointer"
|
||||
loading="lazy"
|
||||
alt="Logout"
|
||||
src="/materialsymbolslogout.svg"
|
||||
onClick={() => handleNavigation('/logout')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
||||
CategoryList.propTypes = {
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default CategoryList;
|
||||
|
@ -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;
|
@ -1,8 +1,7 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import '../../styles/ProductList.css';
|
||||
import { useMemo } from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const ProductRow = ({ className = "", image17, shirts, price, onClick, propLeft, propTop }) => {
|
||||
const ProductRow = ({ className = "", image17, shirts, propLeft, propTop }) => {
|
||||
const productRowStyle = useMemo(() => {
|
||||
return {
|
||||
left: propLeft,
|
||||
@ -12,40 +11,35 @@ const ProductRow = ({ className = "", image17, shirts, price, onClick, propLeft,
|
||||
|
||||
return (
|
||||
<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}
|
||||
>
|
||||
<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"
|
||||
alt={shirts}
|
||||
alt=""
|
||||
src={image17}
|
||||
/>
|
||||
<div className="w-full flex flex-col items-start py-0 px-5">
|
||||
<div className="text-xl mb-2 text-gray-700">
|
||||
<div className="self-stretch flex flex-row items-start justify-center py-0 pr-[21px] pl-5">
|
||||
<div className="relative leading-[53px] lowercase font-semibold inline-block min-w-[124px] mq450:text-10xl mq450:leading-[32px] mq1025:text-19xl mq1025:leading-[42px]">
|
||||
{shirts}
|
||||
</div>
|
||||
<div className="text-lg mb-4 text-gray-500">
|
||||
${price}
|
||||
</div>
|
||||
<div className="flex flex-row items-start justify-start py-0 px-[66px] text-3xl">
|
||||
<div className="relative leading-[110%] lowercase font-semibold mq450:text-lg mq450:leading-[19px]">
|
||||
Click to view products
|
||||
</div>
|
||||
</div>
|
||||
{/* Customize Button */}
|
||||
<button
|
||||
className="customize-button w-full py-2 px-4 "
|
||||
onClick={onClick}
|
||||
>
|
||||
customize
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
ProductRow.propTypes = {
|
||||
className: PropTypes.string,
|
||||
image17: PropTypes.string.isRequired,
|
||||
shirts: PropTypes.string.isRequired,
|
||||
price: PropTypes.string.isRequired, // Ensure price is a required prop
|
||||
onClick: PropTypes.func.isRequired,
|
||||
image17: PropTypes.string,
|
||||
shirts: PropTypes.string,
|
||||
|
||||
/** Style props */
|
||||
propLeft: PropTypes.any,
|
||||
propTop: PropTypes.any,
|
||||
};
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -1,331 +1,148 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import React, { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Sidebar from '../sidebar/Sidebar';
|
||||
import axiosInstance from '../../api/axiosConfig';
|
||||
import { createCustomer } from "../../services_1/customersService";
|
||||
const CLASSES = {
|
||||
container: 'flex flex-row',
|
||||
mainContent: 'w-full flex flex-col min-h-screen text-black',
|
||||
innerContent: 'min-h-screen text-gray-800 p-6',
|
||||
header: 'flex justify-between items-center',
|
||||
headerTitle: 'flex items-center',
|
||||
backButton: 'text-[#0e355b] text-xl',
|
||||
title: 'text-2xl font-semibold text-[#0e355b]',
|
||||
skipLink: 'text-blue-900',
|
||||
formGrid: 'grid grid-cols-1 md:grid-cols-3 gap-6',
|
||||
imageSection: 'space-y-6',
|
||||
imageWrapper: 'text-center',
|
||||
imageContainer: 'inline-block relative',
|
||||
imagePreview: 'bg-gray-300 w-24 h-24 rounded-full mx-auto overflow-hidden',
|
||||
noImage: 'w-full h-full flex items-center justify-center text-gray-500',
|
||||
hiddenInput: 'hidden',
|
||||
uploadButton: 'absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-gray-700 text-white text-xs px-2 py-1 rounded-full',
|
||||
linkTitle: 'text-lg font-semibold',
|
||||
formSection: 'md:col-span-2 space-y-4',
|
||||
formGroup: ' text-gray-800 font-medium',
|
||||
formInput: ' p-2 border-b border-gray-400 focus:border-gray-700',
|
||||
formSelect: ' p-2 border-b border-gray-400 focus:border-gray-700',
|
||||
submitButton: 'bg-[#0e355b] text-white py-2 px-4 rounded-md',
|
||||
};
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
|
||||
const AddCustomer = () => {
|
||||
const navigate = useNavigate();
|
||||
const [customerData, setCustomerData] = useState({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
lastname:'',
|
||||
age: '',
|
||||
gender: '',
|
||||
email: '',
|
||||
address: '',
|
||||
mobile: '',
|
||||
address: {
|
||||
address_line_1: '',
|
||||
address_line_2: '',
|
||||
nearby_landmark: '',
|
||||
pincode: '',
|
||||
city_id: '',
|
||||
},
|
||||
image: null,
|
||||
});
|
||||
|
||||
const [cities, setCities] = useState([]);
|
||||
const [imagePreview, setImagePreview] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchCities = async () => {
|
||||
try {
|
||||
const response = await axiosInstance.get('/cities');
|
||||
if (Array.isArray(response.data)) {
|
||||
setCities(response.data);
|
||||
} else {
|
||||
console.error('Error: Data is not an array', response.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching cities:', error.response ? error.response.data : error.message);
|
||||
}
|
||||
};
|
||||
|
||||
fetchCities();
|
||||
}, []);
|
||||
|
||||
// Updated handleInputChange function
|
||||
const handleInputChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
|
||||
if (name.startsWith('address.')) {
|
||||
const addressField = name.split('.')[1];
|
||||
setCustomerData((prevData) => ({
|
||||
...prevData,
|
||||
address: {
|
||||
...prevData.address,
|
||||
[addressField]: value,
|
||||
},
|
||||
}));
|
||||
} else {
|
||||
setCustomerData((prevData) => ({
|
||||
...prevData,
|
||||
[name]: value,
|
||||
}));
|
||||
}
|
||||
setCustomerData({
|
||||
...customerData,
|
||||
[name]: value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleImageChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
setCustomerData({
|
||||
...customerData,
|
||||
image: file,
|
||||
});
|
||||
setImagePreview(URL.createObjectURL(file));
|
||||
}
|
||||
};
|
||||
|
||||
// Updated handleAddCustomerSubmit function
|
||||
const handleAddCustomerSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData();
|
||||
|
||||
// Append flat fields
|
||||
Object.keys(customerData).forEach((key) => {
|
||||
if (key !== 'address' && key !== 'image') {
|
||||
formData.append(key, customerData[key]);
|
||||
}
|
||||
});
|
||||
|
||||
// Append address fields
|
||||
Object.keys(customerData.address).forEach((key) => {
|
||||
formData.append(`address[${key}]`, customerData.address[key]);
|
||||
});
|
||||
|
||||
// Append image file
|
||||
if (customerData.image) {
|
||||
formData.append('image', customerData.image);
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await createCustomer(customerData);
|
||||
console.log('Customer added successfully:', data);
|
||||
const response = await axiosInstance.post('http://localhost:5000/customers', customerData);
|
||||
navigate('/customers');
|
||||
} catch (error) {
|
||||
console.error('Error adding customer:', error.response || error.message);
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={CLASSES.container}>
|
||||
<div className="flex flex-row">
|
||||
<Sidebar />
|
||||
<div className={CLASSES.mainContent}>
|
||||
<div className={CLASSES.innerContent}>
|
||||
<div className={CLASSES.header}>
|
||||
<div className={CLASSES.headerTitle}>
|
||||
<Link to="/customers" className={CLASSES.backButton}>
|
||||
<FontAwesomeIcon icon={faChevronLeft} />
|
||||
</Link>
|
||||
<h1 className={CLASSES.title}>add customer</h1>
|
||||
</div>
|
||||
<Link to="/CategoryList" className={CLASSES.skipLink}>
|
||||
skip for now
|
||||
</Link>
|
||||
<div className="w-full flex flex-col min-h-screen text-white">
|
||||
<div className="min-h-screen text-gray-300 p-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h1 className="text-2xl font-semibold text-#0e355b">add customer</h1>
|
||||
<Link to="/" className="text-#0e355b">skip for now</Link>
|
||||
</div>
|
||||
<div className={CLASSES.formGrid}>
|
||||
<div className={CLASSES.imageSection}>
|
||||
<div className={CLASSES.imageWrapper}>
|
||||
<div className={CLASSES.imageContainer}>
|
||||
<div className={CLASSES.imagePreview}>
|
||||
{imagePreview ? (
|
||||
<img
|
||||
src={imagePreview}
|
||||
alt="Customer"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className={CLASSES.noImage}>no image</div>
|
||||
)}
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className={CLASSES.hiddenInput}
|
||||
id="imageUpload"
|
||||
onChange={handleImageChange}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
document.getElementById('imageUpload').click()
|
||||
}
|
||||
className={CLASSES.uploadButton}
|
||||
>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div className="space-y-6">
|
||||
<div className="text-center">
|
||||
<div className="inline-block relative">
|
||||
<div className="bg-gray-700 w-24 h-24 rounded-full mx-auto"></div>
|
||||
<button className="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-0e355b text-white text-xs px-2 py-1 rounded-full">
|
||||
{/* Add Customer Image */}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Link to="/add-customer">
|
||||
<h2 className={CLASSES.linkTitle}>customer information</h2>
|
||||
<Link to="/">
|
||||
<h2 className="text-lg font-semibold text-">customer information</h2>
|
||||
</Link>
|
||||
<Link to="/measurements">
|
||||
<h2 className={CLASSES.linkTitle}>measurements</h2>
|
||||
<h2 className="text-lg font-semibold text-">measurements</h2>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className={CLASSES.formSection}>
|
||||
<form onSubmit={handleAddCustomerSubmit} className="space-y-4">
|
||||
<div className={CLASSES.formGroup}>
|
||||
<label htmlFor="firstname">First Name</label>
|
||||
<div className="md:col-span-2 space-y-4">
|
||||
<form onSubmit={handleAddCustomerSubmit}>
|
||||
<div>
|
||||
<label className="block text-d1d5db font-medium text-d1d5db">customer firstname:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="firstname"
|
||||
name="firstname"
|
||||
className={CLASSES.formInput}
|
||||
value={customerData.firstname}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline"
|
||||
/>
|
||||
</div>
|
||||
<div className={CLASSES.formGroup}>
|
||||
<label htmlFor="lastname">Last Name</label>
|
||||
<div>
|
||||
<label className="block text-d1d5db font-medium text-d1d5db">customer lastname:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="lastname"
|
||||
name="lastname"
|
||||
className={CLASSES.formInput}
|
||||
value={customerData.lastname}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline"
|
||||
/>
|
||||
</div>
|
||||
<div className={CLASSES.formGroup}>
|
||||
<label htmlFor="age">Age</label>
|
||||
<div>
|
||||
<label className="block text-d1d5db font-medium text-d1d5db">age:</label>
|
||||
<input
|
||||
type="number"
|
||||
id="age"
|
||||
name="age"
|
||||
className={CLASSES.formInput}
|
||||
value={customerData.age}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline"
|
||||
/>
|
||||
</div>
|
||||
<div className={CLASSES.formGroup}>
|
||||
<label htmlFor="gender">Gender</label>
|
||||
<div>
|
||||
<label className="block text-d1d5db font-medium text-d1d5db">gender:</label>
|
||||
<select
|
||||
id="gender"
|
||||
name="gender"
|
||||
className={CLASSES.formSelect}
|
||||
value={customerData.gender}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline"
|
||||
>
|
||||
<option value="">Select gender</option>
|
||||
<option value="male">Male</option>
|
||||
<option value="female">Female</option>
|
||||
<option value="other">Other</option>
|
||||
<option value="" disabled>select gender</option>
|
||||
<option value="male">male</option>
|
||||
<option value="female">female</option>
|
||||
<option value="other">other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className={CLASSES.formGroup}>
|
||||
<label htmlFor="email">Email</label>
|
||||
<div>
|
||||
<label className="block text-d1d5db font-medium text-d1d5db">email:</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
className={CLASSES.formInput}
|
||||
value={customerData.email}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline"
|
||||
/>
|
||||
</div>
|
||||
<div className={CLASSES.formGroup}>
|
||||
<label htmlFor="mobile">Mobile Number</label>
|
||||
<div>
|
||||
<label className="block text-d1d5db font-medium text-d1d5db">address:</label>
|
||||
<input
|
||||
type="text"
|
||||
name="address"
|
||||
value={customerData.address}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-d1d5db font-medium text-d1d5db">mobile no:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="mobile"
|
||||
name="mobile"
|
||||
className={CLASSES.formInput}
|
||||
value={customerData.mobile}
|
||||
onChange={handleInputChange}
|
||||
className="input-underline"
|
||||
/>
|
||||
</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={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
|
||||
<br />
|
||||
<div className="text-center">
|
||||
<button type="submit" className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md font-semibold text-sm">
|
||||
save information
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -46,15 +46,15 @@
|
||||
}
|
||||
|
||||
.customer-table th {
|
||||
background-color: #ffffff;
|
||||
background-color: #09090B;
|
||||
}
|
||||
|
||||
.customer-table tbody tr:nth-child(odd) {
|
||||
background-color: #ffffff;
|
||||
background-color: #09090B;
|
||||
}
|
||||
|
||||
.customer-table tbody tr:nth-child(even) {
|
||||
background-color: #ffffff;
|
||||
background-color: #09090B;
|
||||
}
|
||||
|
||||
.customer-table tbody tr:hover {
|
@ -2,23 +2,21 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Sidebar from '../sidebar/Sidebar';
|
||||
import axiosInstance from '../../api/axiosConfig';
|
||||
import '../../styles/CustomerList.css';
|
||||
import {getAllCustomers} from '../../services_1/customersService'
|
||||
|
||||
|
||||
|
||||
const CustomerList = () => {
|
||||
const [customers, setCustomers] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch the customers data from the JSON file
|
||||
getCustomers();
|
||||
}, []);
|
||||
|
||||
const getCustomers = async () => {
|
||||
try {
|
||||
const data = await getAllCustomers()
|
||||
setCustomers(data);
|
||||
const response = await axiosInstance.get('http://localhost:5000/customers');
|
||||
let customers_data = response.data; // List of customers
|
||||
setCustomers(customers_data);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
@ -33,18 +31,18 @@ const CustomerList = () => {
|
||||
<Sidebar />
|
||||
<div className="flex-grow p-6 bg-gray-100">
|
||||
<div className="bg-white p-6 rounded-lg shadow">
|
||||
<div className="flex justify-between items-center ">
|
||||
<h1 className="text-2xl font-semibold text-gray-800">customers list</h1>
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h1 className="text-2xl font-semibold text-gray-800">Customers List</h1>
|
||||
<button
|
||||
className="bg-#0e355b text-#d1d5db px-4 py-2 rounded-md text-sm"
|
||||
className="bg-#0e355b text-d1d5db px-4 py-2 rounded-md text-sm"
|
||||
onClick={handleAddCustomer}
|
||||
>
|
||||
add new customer
|
||||
Add New Customer
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center ">
|
||||
<div className="flex items-center mb-4">
|
||||
<svg
|
||||
className="w-5 h-5 text-gray-500"
|
||||
className="w-5 h-5 mr-2 text-gray-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
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"
|
||||
/>
|
||||
</svg>
|
||||
<span className="text-sm text-gray-600">filter</span>
|
||||
<span className="text-sm text-gray-600">Filter</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600 ">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">
|
||||
<thead>
|
||||
<tr className="text-left text-xs uppercase text-gray-600 border-b">
|
||||
|
@ -3,14 +3,13 @@ import { Button, Input, Form } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import Sidebar from '../sidebar/Sidebar';
|
||||
import '../../styles/CustomerMeasurements.css';
|
||||
|
||||
const CustomerMeasurements = () => {
|
||||
const onFinish = async (values) => {
|
||||
const token = localStorage.getItem('token'); // Assuming the token is stored in localStorage
|
||||
|
||||
try {
|
||||
const response = await axios.post('http://localhost:5000/measurements', values, {
|
||||
const response = await axios.post('http://localhost:8080/measurements', values, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
@ -23,57 +22,55 @@ const CustomerMeasurements = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="container flex">
|
||||
<div className="flex flex-row">
|
||||
<Sidebar />
|
||||
<div className="content flex-1">
|
||||
<div className="form-container max-w-2xl mx-auto p-6">
|
||||
<div className="header flex justify-between items-left mb-4">
|
||||
<h1 className="mml-4 text-2xl font-semibold text-gray-700">customer measurements</h1>
|
||||
<Link to="/CategoryList" className="skip-link ">skip for now</Link>
|
||||
<div className="w-full flex flex-col min-h-screen text-white">
|
||||
<div className="min-h-screen text-#4b5563 p-6">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h1 className="text-2xl font-semibold text-#0e355b">Customer Measurements</h1>
|
||||
<Link to="/CategoryList" className="text-#0e355b">skip for now</Link>
|
||||
</div>
|
||||
<Form onFinish={onFinish} className="form space-y-4">
|
||||
<h2 className="section-title text-lg font-medium">Upper Body Measurements</h2>
|
||||
<Form.Item name="neck" label="Neck Circumference (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form onFinish={onFinish} className="space-y-4">
|
||||
<h2 className="text-lg font-semibold text-#0e355b">Upper Body Measurements</h2>
|
||||
<Form.Item name="neck" label={<span className="text-#0e355b">Neck Circumference (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item name="chest" label="Chest Circumference (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form.Item name="chest" label={<span className="text-#0e355b">Chest Circumference (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item name="waist" label="Waist Circumference (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form.Item name="waist" label={<span className="text-#0e355b">Waist Circumference (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item name="shoulder" label="Shoulder Width (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form.Item name="shoulder" label={<span className="text-#0e355b">Shoulder Width (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item name="arm" label="Arm Length (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form.Item name="arm" label={<span className="text-#0e355b">Arm Length (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item name="sleeve" label="Sleeve Length (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form.Item name="sleeve" label={<span className="text-#0e355b">Sleeve Length (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
|
||||
<h2 className="section-title text-lg font-medium">Lower Body Measurements</h2>
|
||||
<Form.Item name="hip" label="Hip Circumference (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<h2 className="text-lg font-semibold text-#0e355b">Lower Body Measurements</h2>
|
||||
<Form.Item name="hip" label={<span className="text-#0e355b">Hip Circumference (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item name="inseam" label="Inseam (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form.Item name="inseam" label={<span className="text-#0e355b">Inseam (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item name="outseam" label="Outseam (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form.Item name="outseam" label={<span className="text-#0e355b">Out Seam (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item name="thigh" label="Thigh Circumference (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form.Item name="thigh" label={<span className="text-#0e355b">Thigh Circumference (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item name="ankle" label="Ankle Circumference (in inch)">
|
||||
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
|
||||
<Form.Item name="ankle" label={<span className="text-#0e355b">Ankle Circumference (in inch)</span>}>
|
||||
<Input className="block text-sm font-medium text-#4b5563" />
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<div className="button-container flex justify-end">
|
||||
<Button type="primary" htmlType="submit" className="submit-button bg-[#0e355b] text-white py-2 px-4 rounded-md">
|
||||
Save Measurements
|
||||
</Button>
|
||||
</div>
|
||||
<Button type="primary" htmlType="submit" className="w-full py-4 bg-#0e355b text-#d1d5db rounded-md font-semibold mb-4 hover:bg-#154676">
|
||||
Save Measurements
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
19
src/components/employee/Dashboard.css
Normal file
19
src/components/employee/Dashboard.css
Normal 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;
|
||||
}
|
49
src/components/employee/EmployeeDashboard.jsx
Normal file
49
src/components/employee/EmployeeDashboard.jsx
Normal 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;
|
7
src/components/order/ViewOrders.jsx
Normal file
7
src/components/order/ViewOrders.jsx
Normal file
@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
const ViewOrders = () => {
|
||||
return <div>View Orders Page</div>;
|
||||
};
|
||||
|
||||
export default ViewOrders;
|
@ -1,8 +1,10 @@
|
||||
// src/components/sidebar/AdminSidebar.js
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Layout } from 'antd';
|
||||
import { Layout, Drawer, Button } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import BrookslogoIcon from '../../assets/thob-data/BrookslogoIcon.svg';
|
||||
import HomeIcon from '../../assets/thob-data/HomeIcon.svg';
|
||||
import AddCustomerIcon from '../../assets/thob-data/AddCustomerIcon.svg';
|
||||
@ -13,62 +15,61 @@ import EmployeeIcon from '../../assets/thob-data/EmployeeIcon.svg';
|
||||
import MeasurmentsIcon from '../../assets/thob-data/MeasurmentsIcon.svg';
|
||||
import ProfileIcon from '../../assets/thob-data/ProfileIcon.svg';
|
||||
import LogoutIcon from '../../assets/thob-data/LogoutIcon.svg';
|
||||
import '../../styles/AdminSidebar.css'; // Import the CSS file
|
||||
|
||||
const { Sider } = Layout;
|
||||
|
||||
const classNames = {
|
||||
sider: 'desktop-sidebar',
|
||||
logoContainer: 'flex items-center justify-center h-16 w-16 m-5 bg-white',
|
||||
iconContainer: 'flex flex-col items-center',
|
||||
iconLink: 'my-6',
|
||||
icon: 'hover:opacity-75 sidebar-icon',
|
||||
};
|
||||
|
||||
const AdminSidebar = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const showDrawer = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const closeDrawer = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Sider
|
||||
className={classNames.sider}
|
||||
className="desktop-sidebar"
|
||||
breakpoint="lg"
|
||||
collapsedWidth="0"
|
||||
width={80}
|
||||
style={{ height: '100vh' }}
|
||||
>
|
||||
<div className={classNames.logoContainer} style={{ width: '40px', height: '40px', marginTop: '16px', marginLeft: '20px' }}>
|
||||
<Link to="/admin" className={classNames.iconLink}>
|
||||
<img src={BrookslogoIcon} alt="Home" className={classNames.icon} />
|
||||
<div className="flex items-center justify-center h-16 w-16 m-5 bg-white" style={{ width: '40px', height: '40px', marginTop: '16px', marginLeft: '20px' }}>
|
||||
<Link to="/admin" className="my-6">
|
||||
<img src={BrookslogoIcon} alt="Home" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classNames.iconContainer}>
|
||||
<Link to="/admin" className={classNames.iconLink}>
|
||||
<img src={HomeIcon} alt="Home" className={classNames.icon} />
|
||||
<div className="flex flex-col items-center">
|
||||
<Link to="/admin/home" className="my-6">
|
||||
<img src={HomeIcon} alt="Home" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/CategoryList" className={classNames.iconLink}>
|
||||
<img src={CategoriesIcon} alt="Categories" className={classNames.icon} />
|
||||
<Link to="/admin/categories" className="my-6">
|
||||
<img src={CategoriesIcon} alt="Categories" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/admin/add-customer" className={classNames.iconLink}>
|
||||
<img src={AddCustomerIcon} alt="Add Customer" className={classNames.icon} />
|
||||
<Link to="/admin/add-customer" className="my-6">
|
||||
<img src={AddCustomerIcon} alt="Add Customer" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/admin/orders" className={classNames.iconLink}>
|
||||
<img src={OrdersIcon} alt="Orders" className={classNames.icon} />
|
||||
<Link to="/admin/orders" className="my-6">
|
||||
<img src={OrdersIcon} alt="Orders" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/admin/catalog" className={classNames.iconLink}>
|
||||
<img src={CatalogIcon} alt="Catalog" className={classNames.icon} />
|
||||
<Link to="/admin/catalog" className="my-6">
|
||||
<img src={CatalogIcon} alt="Catalog" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/employees" className={classNames.iconLink}>
|
||||
<img src={EmployeeIcon} alt="Employee" className={classNames.icon} />
|
||||
<Link to="/admin/employee" className="my-6">
|
||||
<img src={EmployeeIcon} alt="Employee" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/setmeasurements" className={classNames.iconLink}>
|
||||
<img src={MeasurmentsIcon} alt="Measurements" className={classNames.icon} />
|
||||
<Link to="/admin/measurements" className="my-6">
|
||||
<img src={MeasurmentsIcon} alt="Measurements" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/admin/profile" className={classNames.iconLink}>
|
||||
<img src={ProfileIcon} alt="Profile" className={classNames.icon} />
|
||||
<Link to="/admin/profile" className="my-6">
|
||||
<img src={ProfileIcon} alt="Profile" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/admin/logout" className={classNames.iconLink}>
|
||||
<img src={LogoutIcon} alt="Logout" className={classNames.icon} />
|
||||
<Link to="/admin/logout" className="my-6">
|
||||
<img src={LogoutIcon} alt="Logout" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
</div>
|
||||
</Sider>
|
||||
|
@ -1,6 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Layout } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { Layout, Drawer, Button } from 'antd';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
MenuFoldOutlined,
|
||||
MenuUnfoldOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import BrookslogoIcon from '../../assets/thob-data/BrookslogoIcon.svg';
|
||||
import HomeIcon from '../../assets/thob-data/HomeIcon.svg';
|
||||
import AddCustomerIcon from '../../assets/thob-data/AddCustomerIcon.svg';
|
||||
@ -8,60 +12,62 @@ import OrdersIcon from '../../assets/thob-data/OrdersIcon.svg';
|
||||
import CategoriesIcon from '../../assets/thob-data/CategoriesIcon.svg';
|
||||
import ProfileIcon from '../../assets/thob-data/ProfileIcon.svg';
|
||||
import LogoutIcon from '../../assets/thob-data/LogoutIcon.svg';
|
||||
import '../../styles/EmployeeSidebar.css';
|
||||
|
||||
const { Sider } = Layout;
|
||||
|
||||
const classNames = {
|
||||
sider: 'desktop-sidebar',
|
||||
container: 'flex flex-col items-center mt-4 h-full justify-between',
|
||||
logoContainer: 'flex items-center justify-center h-16 w-16 mb-5 bg-white',
|
||||
iconContainer: 'flex flex-col items-center flex-grow',
|
||||
iconLink: 'my-6',
|
||||
icon: 'hover:opacity-75 sidebar-icon',
|
||||
logoutContainer: 'flex items-center justify-center mb-4',
|
||||
};
|
||||
|
||||
const EmployeeSidebar = () => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
|
||||
const showDrawer = () => {
|
||||
setVisible(true);
|
||||
};
|
||||
|
||||
const closeDrawer = () => {
|
||||
setVisible(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<Sider
|
||||
className={classNames.sider}
|
||||
breakpoint="lg"
|
||||
collapsedWidth="0"
|
||||
width={70}
|
||||
style={{ height: '100vh', display: 'flex', flexDirection: 'column', justifyContent: 'space-between' }}
|
||||
>
|
||||
<div className={classNames.container}>
|
||||
<div className={classNames.logoContainer}>
|
||||
<Link to="">
|
||||
<img src={BrookslogoIcon} alt="Brooks Bingham Logo" className={classNames.icon} />
|
||||
<>
|
||||
|
||||
<Sider
|
||||
className="desktop-sidebar"
|
||||
breakpoint="lg"
|
||||
collapsedWidth="0"
|
||||
width={70}
|
||||
style={{ height: '100vh' }}
|
||||
>
|
||||
<div className="flex items-center justify-center h-16 w-16 m-5 bg-white" style={{ width: '40px', height: '40px', marginTop: '16px', marginLeft: '20px' }}>
|
||||
<Link to="/employee" className="my-6">
|
||||
<img src={BrookslogoIcon} alt="Home" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
</div>
|
||||
<div className={classNames.iconContainer}>
|
||||
<Link to="/employee/home" className={classNames.iconLink}>
|
||||
<img src={HomeIcon} alt="Home" className={classNames.icon} />
|
||||
<div className="flex flex-col items-center">
|
||||
<Link to="/employee/home" className="my-6">
|
||||
<img src={HomeIcon} alt="Home" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/CategoryList" className={classNames.iconLink}>
|
||||
<img src={CategoriesIcon} alt="Categories" className={classNames.icon} />
|
||||
<Link to="/employee/categories" className="my-6">
|
||||
<img src={CategoriesIcon} alt="Categories" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/customers" className={classNames.iconLink}>
|
||||
<img src={AddCustomerIcon} alt="Add Customer" className={classNames.icon} />
|
||||
<Link to="/customers" className="my-6">
|
||||
<img src={AddCustomerIcon} alt="Add Customer" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/order" className={classNames.iconLink}>
|
||||
<img src={OrdersIcon} alt="Orders" className={classNames.icon} />
|
||||
<Link to="/employee/orders" className="my-6">
|
||||
<img src={OrdersIcon} alt="Orders" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/profile" className={classNames.iconLink}>
|
||||
<img src={ProfileIcon} alt="Profile" className={classNames.icon} />
|
||||
<Link to="/employee/profile" className="my-6">
|
||||
<img src={ProfileIcon} alt="Profile" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
<Link to="/employee/logout" className="my-6">
|
||||
<img src={LogoutIcon} alt="Logout" className="hover:opacity-75 sidebar-icon" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classNames.logoutContainer}>
|
||||
<Link to="/employee/logout">
|
||||
<img src={LogoutIcon} alt="Logout" className={classNames.icon} />
|
||||
</Link>
|
||||
</div>
|
||||
</Sider>
|
||||
</Sider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmployeeSidebar;
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -56,7 +56,4 @@
|
||||
.menu-item {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,19 +1,12 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import AdminSidebar from './AdminSidebar';
|
||||
import EmployeeSidebar from './EmployeeSidebar';
|
||||
import { AuthContext } from '../../contexts/AuthContext';
|
||||
|
||||
const Sidebar = () => {
|
||||
const [user, setUserData] = useState(null);
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
useEffect(() => {
|
||||
// Retrieve user data from localStorage
|
||||
const storedUserData = localStorage.getItem('loggedInUser');
|
||||
if (storedUserData) {
|
||||
setUserData(JSON.parse(storedUserData));
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!user) return null; // Ensure user is not null before accessing its properties
|
||||
if (!user) return null;
|
||||
|
||||
return user.role === 'admin' ? <AdminSidebar /> : <EmployeeSidebar />;
|
||||
};
|
||||
|
@ -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;
|
@ -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;
|
@ -1,36 +1,17 @@
|
||||
const authService = {
|
||||
login: async (username, password) => {
|
||||
try {
|
||||
// Simulate login process with API call (replace with real API endpoint)
|
||||
const response = await axiosInstance.post('/auth/login', { username, password });
|
||||
const userData = response.data;
|
||||
|
||||
// Store user data and token in localStorage
|
||||
login: async (username, password) => {
|
||||
// Simulate login process
|
||||
const userData = { username, role: username === 'admin' ? 'admin' : 'employee' };
|
||||
localStorage.setItem('user', JSON.stringify(userData));
|
||||
localStorage.setItem('token', userData.token);
|
||||
|
||||
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');
|
||||
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;
|
||||
|
||||
export default authService;
|
||||
|
@ -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}`);
|
||||
});
|
@ -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;
|
@ -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);
|
||||
}
|
||||
};
|
@ -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);
|
||||
}
|
||||
};
|
@ -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);
|
||||
}
|
||||
};
|
@ -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);
|
||||
}
|
||||
};
|
@ -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);
|
||||
}
|
||||
};
|
@ -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);
|
||||
}
|
||||
};
|
@ -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 */
|
||||
}
|
@ -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 */
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
@ -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 */
|
||||
}
|
||||
|
@ -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 */
|
||||
}
|
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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 */
|
||||
}
|
||||
|
@ -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 */
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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',
|
||||
|
||||
|
||||
};
|
||||
|
@ -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;
|
@ -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
10
tailwind.config.js
Normal file
@ -0,0 +1,10 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./src/**/*.{js,jsx,ts,tsx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
@ -1,12 +1,7 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'path';
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user