added product categories pages to the application
This commit is contained in:
parent
b10f323455
commit
04b3c4e07b
14
.env
14
.env
@ -1,2 +1,12 @@
|
|||||||
VITE_ENVIRONMENT=dev
|
|
||||||
VITE_API_URL=https://api.yourproductionurl.com
|
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));
|
||||||
|
12
src/App.jsx
12
src/App.jsx
@ -14,6 +14,13 @@ 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 EmployeeProfile from "./components/admin/EmployeeProfile";
|
||||||
import CategoryList from "./components/categories/CategoryList";
|
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";
|
||||||
|
import Customize from "./components/customize/Customize";
|
||||||
|
|
||||||
|
|
||||||
import { Refine } from "@refinedev/core";
|
import { Refine } from "@refinedev/core";
|
||||||
import { RefineKbarProvider } from "@refinedev/kbar";
|
import { RefineKbarProvider } from "@refinedev/kbar";
|
||||||
import { ToastContainer, toast } from "react-toastify";
|
import { ToastContainer, toast } from "react-toastify";
|
||||||
@ -46,6 +53,11 @@ const App = () => {
|
|||||||
<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="/employee-profile" element={<PrivateRoute><EmployeeProfile /></PrivateRoute>} />
|
||||||
<Route path="/CategoryList" element={<PrivateRoute><CategoryList /></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 />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Content>
|
</Content>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,10 +2,12 @@ import axios from 'axios';
|
|||||||
|
|
||||||
// Determine environment and set baseURL accordingly
|
// Determine environment and set baseURL accordingly
|
||||||
const environment = import.meta.env.VITE_ENVIRONMENT;
|
const environment = import.meta.env.VITE_ENVIRONMENT;
|
||||||
const baseURL = environment === 'dev'
|
const baseURL = environment === 'production'
|
||||||
? import.meta.env.VITE_API_URL
|
? import.meta.env.VITE_API_URL
|
||||||
: 'http://localhost:5000'; // Default for development
|
: '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,
|
||||||
headers: {
|
headers: {
|
||||||
@ -13,18 +15,23 @@ const axiosInstance = axios.create({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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 => {
|
||||||
|
// Handle request error
|
||||||
|
console.error('Request error:', error);
|
||||||
|
return Promise.reject(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Response interceptor to handle responses globally
|
||||||
axiosInstance.interceptors.response.use(
|
axiosInstance.interceptors.response.use(
|
||||||
response => response,
|
response => response,
|
||||||
error => {
|
error => {
|
||||||
// Handle errors globally
|
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
// Server responded with a status other than 2xx
|
// Server responded with a status other than 2xx
|
||||||
console.error('Error response:', error.response.data);
|
console.error('Error response:', error.response.data);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import axiosInstance from './axiosConfig';
|
import axiosInstance from '../axiosConfig';
|
||||||
|
|
||||||
|
|
||||||
export const getEmployees = async () => {
|
export const getEmployees = async () => {
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
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'; // Ensure the correct path to Sidebar
|
import Sidebar from '../sidebar/Sidebar';
|
||||||
|
import '../../styles/ProductList.css';
|
||||||
|
|
||||||
const CategoryList = () => {
|
const CategoryList = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
// Updated categories array with names and specifications
|
||||||
const categories = [
|
const categories = [
|
||||||
{ name: 'shirts', image: 'https://www2.hm.com/en_in/productpage.1032522123.html' },
|
{ name: 'Shirts', image: 'https://www2.hm.com/en_in/productpage.1032522123.html', description: 'shirts' },
|
||||||
{ name: 'suits', image: 'https://www2.hm.com/en_in/productpage.1032522123.html' },
|
{ name: 'Suits', image: 'https://www2.hm.com/en_in/productpage.1032522123.html', description: 'suits' },
|
||||||
{ name: 'tuxedos', image: 'https://www2.hm.com/en_in/productpage.1032522123.html' },
|
{ name: 'Tuxedos', image: 'https://www2.hm.com/en_in/productpage.1032522123.html', description: 'tuxedos' },
|
||||||
{ name: 'jackets', image: 'https://image.coolblue.nl/422x390/products/1101120' },
|
{ name: 'Jackets', image: 'https://image.coolblue.nl/422x390/products/1101120', description: 'jackets' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleClick = (category) => {
|
const handleClick = (category) => {
|
||||||
@ -22,20 +24,25 @@ const CategoryList = () => {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
|
|
||||||
{/* Main content */}
|
{/* Main content */}
|
||||||
<div className="flex-grow p-8">
|
<div className="flex-grow flex flex-col">
|
||||||
<h1 className="text-3xl font-bold mb-8 text-left pl-4">categories</h1>
|
<div className="bg-white p-4 flex items-center">
|
||||||
<div className="grid grid-cols-2 gap-8">
|
<h1 className="ml-4 text-2xl font-medium text-gray-700">Categories</h1>
|
||||||
{categories.map((category, index) => (
|
</div>
|
||||||
<div
|
<div className="flex-grow p-8">
|
||||||
key={index}
|
<div className="grid grid-cols-2 gap-8">
|
||||||
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"
|
{categories.map((category, index) => (
|
||||||
onClick={() => handleClick(category.name)}
|
<div
|
||||||
>
|
key={index}
|
||||||
<img src={category.image} alt={category.name} className="w-48 h-48 object-cover" />
|
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"
|
||||||
<h2 className="text-xl font-bold text-center mt-2 capitalize">{category.name}</h2>
|
onClick={() => handleClick(category.name.toLowerCase())} // Updated to handle lowercase category names
|
||||||
<p className="text-center text-sm text-gray-600">click to view products</p>
|
>
|
||||||
</div>
|
<img src={category.image} alt={category.name} className="w-48 h-48 object-cover" />
|
||||||
))}
|
<h2 className="text-xl font-bold text-center mt-2 capitalize">{category.name}</h2>
|
||||||
|
<p className="ml-4 text-2xl font-medium text-gray-700">{category.description}</p>
|
||||||
|
<p className="text-center text-sm text-gray-600 mt-1">Click to view products</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
66
src/components/categories/JacketsProductList.jsx
Normal file
66
src/components/categories/JacketsProductList.jsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Sidebar from '../sidebar/Sidebar';
|
||||||
|
import ProductRow from './ProductRow';
|
||||||
|
import '../../styles/ProductList.css';
|
||||||
|
|
||||||
|
const products = [
|
||||||
|
{ id: 1, name: 'red bomber jacket', price: 129, image: 'path/to/red-bomber-jacket-image.png' },
|
||||||
|
{ id: 2, name: 'green utility jacket', price: 149, image: 'path/to/green-utility-jacket-image.png' },
|
||||||
|
{ id: 3, name: 'black leather jacket', price: 199, image: 'path/to/black-leather-jacket-image.png' },
|
||||||
|
{ id: 4, name: 'blue aces', price: 99, image: 'path/to/blue-aces-image.png' },
|
||||||
|
{ id: 5, name: 'blue birdseye', price: 99, image: 'path/to/blue-birdseye-image.png' },
|
||||||
|
{ id: 6, name: 'blue hydrangea', price: 99, image: 'path/to/blue-hydrangea-image.png' },
|
||||||
|
];
|
||||||
|
|
||||||
|
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,7 +1,8 @@
|
|||||||
import { useMemo } from "react";
|
import React, { useMemo } from 'react';
|
||||||
import PropTypes from "prop-types";
|
import PropTypes from 'prop-types';
|
||||||
|
import '../../styles/ProductList.css';
|
||||||
|
|
||||||
const ProductRow = ({ className = "", image17, shirts, propLeft, propTop }) => {
|
const ProductRow = ({ className = "", image17, shirts, onClick, propLeft, propTop }) => {
|
||||||
const productRowStyle = useMemo(() => {
|
const productRowStyle = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
left: propLeft,
|
left: propLeft,
|
||||||
@ -11,37 +12,43 @@ const ProductRow = ({ className = "", image17, shirts, propLeft, propTop }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`h-[350px] w-[350px] !m-[0] absolute top-[20px] left-[20px] rounded-3xs box-border flex flex-col items-start justify-start py-5 px-0 gap-[15px] text-center text-29xl text-tertiary font-button border-[0.7px] border-solid border-tertiary ${className}`}
|
className={`h-88 w-88 m-0 rounded-lg flex flex-col items-start justify-start py-5 px-0 gap-4 text-center text-3xl font-semibold border border-gray-300 ${className}`}
|
||||||
style={productRowStyle}
|
style={productRowStyle}
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="self-stretch flex-1 relative max-w-full overflow-hidden max-h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
alt=""
|
alt={shirts}
|
||||||
src={image17}
|
src={image17}
|
||||||
/>
|
/>
|
||||||
<div className="self-stretch flex flex-row items-start justify-center py-0 pr-[21px] pl-5">
|
<div className="w-full flex flex-row items-center justify-center py-0 pr-5 pl-5">
|
||||||
<div className="relative leading-[53px] lowercase font-semibold inline-block min-w-[124px] mq450:text-10xl mq450:leading-[32px] mq1025:text-19xl mq1025:leading-[42px]">
|
<div className="leading-9 font-semibold">
|
||||||
{shirts}
|
{shirts}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row items-start justify-start py-0 px-[66px] text-3xl">
|
<div className="w-full flex flex-row items-start justify-start py-0 px-16 text-lg">
|
||||||
<div className="relative leading-[110%] lowercase font-semibold mq450:text-lg mq450:leading-[19px]">
|
<div className="leading-tight font-semibold">
|
||||||
Click to view products
|
Click to view product details
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Customize Button */}
|
||||||
|
<button
|
||||||
|
className="customize-button w-full "
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
Customize
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
ProductRow.propTypes = {
|
ProductRow.propTypes = {
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
image17: PropTypes.string,
|
image17: PropTypes.string.isRequired,
|
||||||
shirts: PropTypes.string,
|
shirts: PropTypes.string.isRequired,
|
||||||
|
onClick: PropTypes.func.isRequired,
|
||||||
/** Style props */
|
|
||||||
propLeft: PropTypes.any,
|
propLeft: PropTypes.any,
|
||||||
propTop: PropTypes.any,
|
propTop: PropTypes.any,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ProductRow;
|
export default ProductRow;
|
||||||
|
66
src/components/categories/ShirtsProductList.jsx
Normal file
66
src/components/categories/ShirtsProductList.jsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Sidebar from '../sidebar/Sidebar';
|
||||||
|
import ProductRow from './ProductRow';
|
||||||
|
import '../../styles/ProductList.css';
|
||||||
|
|
||||||
|
const products = [
|
||||||
|
{ id: 1, name: 'blue aces', price: 99, image: 'path/to/blue-aces-image.png' },
|
||||||
|
{ id: 2, name: 'blue birdseye', price: 99, image: 'path/to/blue-birdseye-image.png' },
|
||||||
|
{ id: 3, name: 'blue hydrangea', price: 99, image: 'path/to/blue-hydrangea-image.png' },
|
||||||
|
{ id: 4, name: 'blue aces', price: 99, image: 'path/to/blue-aces-image.png' },
|
||||||
|
{ id: 5, name: 'blue birdseye', price: 99, image: 'path/to/blue-birdseye-image.png' },
|
||||||
|
{ id: 6, name: 'blue hydrangea', price: 99, image: 'path/to/blue-hydrangea-image.png' },
|
||||||
|
];
|
||||||
|
|
||||||
|
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;
|
66
src/components/categories/SuitsProductList.jsx
Normal file
66
src/components/categories/SuitsProductList.jsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Sidebar from '../sidebar/Sidebar';
|
||||||
|
import ProductRow from './ProductRow';
|
||||||
|
import '../../styles/ProductList.css';
|
||||||
|
|
||||||
|
const products = [
|
||||||
|
{ id: 1, name: 'blue aces', price: 99, image: 'path/to/blue-aces-image.png' },
|
||||||
|
{ id: 2, name: 'blue birdseye', price: 99, image: 'path/to/blue-birdseye-image.png' },
|
||||||
|
{ id: 3, name: 'blue hydrangea', price: 99, image: 'path/to/blue-hydrangea-image.png' },
|
||||||
|
{ id: 4, name: 'blue aces', price: 99, image: 'path/to/blue-aces-image.png' },
|
||||||
|
{ id: 5, name: 'blue birdseye', price: 99, image: 'path/to/blue-birdseye-image.png' },
|
||||||
|
{ id: 6, name: 'blue hydrangea', price: 99, image: 'path/to/blue-hydrangea-image.png' },
|
||||||
|
];
|
||||||
|
|
||||||
|
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} // 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 SuitsProductList;
|
66
src/components/categories/TuxedosProductList.jsx
Normal file
66
src/components/categories/TuxedosProductList.jsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import Sidebar from '../sidebar/Sidebar';
|
||||||
|
import ProductRow from './ProductRow';
|
||||||
|
import '../../styles/ProductList.css';
|
||||||
|
|
||||||
|
const products = [
|
||||||
|
{ id: 1, name: 'black tuxedo', price: 199, image: 'path/to/black-tuxedo-image.png' },
|
||||||
|
{ id: 2, name: 'navy tuxedo', price: 199, image: 'path/to/navy-tuxedo-image.png' },
|
||||||
|
{ id: 3, name: 'grey tuxedo', price: 199, image: 'path/to/grey-tuxedo-image.png' },
|
||||||
|
{ id: 4, name: 'blue aces', price: 99, image: 'path/to/blue-aces-image.png' },
|
||||||
|
{ id: 5, name: 'blue birdseye', price: 99, image: 'path/to/blue-birdseye-image.png' },
|
||||||
|
{ id: 6, name: 'blue hydrangea', price: 99, image: 'path/to/blue-hydrangea-image.png' },
|
||||||
|
];
|
||||||
|
|
||||||
|
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;
|
106
src/components/customize/Customize.jsx
Normal file
106
src/components/customize/Customize.jsx
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import React, { useState } 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 handleStyleChange = (event) => {
|
||||||
|
setStyle(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMaterialChange = (event) => {
|
||||||
|
setMaterial(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
const customization = {
|
||||||
|
productId: id,
|
||||||
|
style,
|
||||||
|
material,
|
||||||
|
};
|
||||||
|
|
||||||
|
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('/');
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen ">
|
||||||
|
{/* Sidebar */}
|
||||||
|
<Sidebar />
|
||||||
|
|
||||||
|
{/* Main content */}
|
||||||
|
<div className="flex-grow flex flex-col">
|
||||||
|
<div className="bg-white p-4 flex items-center">
|
||||||
|
<Link to="/CategoryList" className="">
|
||||||
|
<FontAwesomeIcon icon={faChevronLeft} />
|
||||||
|
</Link>
|
||||||
|
<h1 className="ml-4 text-2xl font-medium text-gray-700 ">Customize Product</h1>
|
||||||
|
</div>
|
||||||
|
<div className="flex-grow flex items-center justify-center p-8">
|
||||||
|
<div className="bg-white p-6 rounded-lg shadow-lg w-full max-w-md">
|
||||||
|
<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 "
|
||||||
|
>
|
||||||
|
<option value="">Select style</option>
|
||||||
|
<option value="collar">Collar</option>
|
||||||
|
<option value="cuff">Cuff</option>
|
||||||
|
<option value="placket">Placket</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<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 "
|
||||||
|
>
|
||||||
|
<option value="">Select material</option>
|
||||||
|
<option value="cotton">Cotton</option>
|
||||||
|
<option value="polyester">Polyester</option>
|
||||||
|
<option value="linen">Linen</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={handleSave}
|
||||||
|
className="w-full py-2 px-4"
|
||||||
|
>
|
||||||
|
Save Style and Material
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Customize;
|
@ -1,4 +1,4 @@
|
|||||||
import axiosInstance from "../api/axiosConfig"; // Import the global axios instance
|
import axiosInstance from "../api/axiosConfig";
|
||||||
|
|
||||||
// Get all users with their metadata
|
// Get all users with their metadata
|
||||||
export const getAllUsers = async () => {
|
export const getAllUsers = async () => {
|
||||||
|
82
src/styles/Customize.css
Normal file
82
src/styles/Customize.css
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1.5rem; /* text-2xl */
|
||||||
|
font-weight: 600; /* font-semibold */
|
||||||
|
color: #0e355b; /* Dark blue */
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back-button {
|
||||||
|
color: #0e355b;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner-content {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 1.5rem;
|
||||||
|
background-color: #f9fafb;
|
||||||
|
color: #4a5568;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.form-input,
|
||||||
|
.form-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem; /* p-3 */
|
||||||
|
border: 1px solid #cbd5e0; /* border-gray-400 */
|
||||||
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:focus,
|
||||||
|
.form-select:focus {
|
||||||
|
border-color: #2d3748; /* Darker gray border on focus */
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button {
|
||||||
|
background-color: #0e355b; /* Dark blue */
|
||||||
|
color: white;
|
||||||
|
padding: 0.75rem 1.5rem; /* p-3 */
|
||||||
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
|
font-weight: 600; /* font-semibold */
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submit-button:hover {
|
||||||
|
background-color: #1e3a8a; /* Darker blue on hover */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
107
src/styles/ProductList.css
Normal file
107
src/styles/ProductList.css
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
.product-list {
|
||||||
|
padding: 20px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-list h1 {
|
||||||
|
color: #0e355b;
|
||||||
|
text-align: left;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: #0e355b;
|
||||||
|
color: #ffffff;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-button:hover {
|
||||||
|
background-color: #154676;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(1, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 640px) {
|
||||||
|
.product-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.product-grid {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #e8e8e8;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: box-shadow 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-row:hover {
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-row img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-row .product-name {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #0e355b;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-row .view-products {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #0e355b;
|
||||||
|
padding: 10px 20px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-row .view-products:hover {
|
||||||
|
background-color: #154676;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-button {
|
||||||
|
background-color: #0e355b;
|
||||||
|
color: #d1d5db;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-button:hover {
|
||||||
|
background-color: #154676;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user