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