Added new files for API services and common components.

This commit is contained in:
surajb 2024-07-29 14:17:08 +05:30
parent caf8d4abc0
commit 261cf4cfbd
21 changed files with 12137 additions and 2621 deletions

214
db.json
View File

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

3918
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@
"@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",
@ -33,17 +34,27 @@
"react-toastify": "^10.0.5"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.8",
"@testing-library/react": "^16.0.0",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^9.8.8",
"axios-mock-adapter": "^1.22.0",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"jest": "^27.5.1",
"json-server": "^1.0.0-beta.1",
"postcss": "^7.0.39",
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
"vite": "^5.3.4"
},
"jest": {
"testEnvironment": "jsdom",
"moduleNameMapper": {
"\\.(css|less|scss|sass)$": "identity-obj-proxy"
}
}
}

View File

@ -18,6 +18,7 @@ import { RefineKbarProvider } from "@refinedev/kbar";
import { ToastContainer, toast } from "react-toastify";
import 'react-toastify/dist/ReactToastify.css';
const { Content } = Layout;
const App = () => {

View File

@ -1,20 +1,19 @@
import axios from 'axios';
const axiosInstance = axios.create({
baseURL: '/api',
baseURL: 'http://localhost:5000',
headers: {
'Content-Type': 'application/json',
},
});
axiosInstance.interceptors.request.use((config) => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
}, (error) => {
return Promise.reject(error);
});
export default axiosInstance;

View File

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

View File

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

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

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

View File

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

View File

@ -93,7 +93,7 @@ const AddEmployee = () => {
});
try {
await axiosInstance.post('/employees', formData, {
await axiosInstance.get('http://localhost:5000/employees', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
@ -144,7 +144,7 @@ const AddEmployee = () => {
onClick={() => document.getElementById('imageUpload').click()}
className={CLASSES.uploadButton}
>
upload
add employee image
</button>
</div>
</div>

View File

@ -1,124 +0,0 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import ProductRow from "./ProductRow";
import PropTypes from "prop-types";
const CategoryList = ({ className = "" }) => {
const navigate = useNavigate();
const handleNavigation = (path) => {
navigate(path);
};
return (
<div
className={`w-full bg-primary overflow-y-auto flex flex-row items-start justify-start gap-[3px] leading-[normal] tracking-[normal] text-center text-29xl text-primary1 font-button mq1100:flex-wrap ${className}`}
>
<div className="flex flex-col items-start justify-start py-0 pr-[33px] pl-0">
<div className="overflow-hidden flex flex-col items-start justify-start pt-4 px-5 pb-[50px] gap-[235.5px] mq450:pb-[21px] mq450:box-border mq1025:pt-5 mq1025:pb-8 mq1025:box-border">
<div className="flex flex-col items-start justify-start gap-[146.5px]">
<div className="w-10 h-[147px] relative">
<img
className="absolute h-[27.21%] w-full top-[0%] right-[0%] bottom-[72.79%] left-[0%] max-w-full overflow-hidden max-h-full"
loading="lazy"
alt=""
src="/brooks-logo.svg"
/>
<div className="absolute top-[70px] left-[calc(50%_-_20px)] w-10 h-[77px] hidden">
<div className="absolute top-[0px] left-[0px] w-full h-full hidden">
<div className="absolute top-[0px] left-[0px] rounded-[50px] box-border w-full h-full border-[1px] border-solid border-secondary-accent" />
<div className="absolute top-[40px] left-[3px] rounded-[50%] bg-secondary-accent w-[34px] h-[34px]" />
</div>
</div>
</div>
<div className="flex flex-row items-start justify-start py-0 pr-2 pl-[7px]">
<div className="flex flex-col items-start justify-start gap-[70px]">
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Home"
src="/home-icon.svg"
onClick={() => handleNavigation('/home')}
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Categories"
src="/category-icon.svg"
onClick={() => handleNavigation('/categories')}
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Customer List"
src="/customer-list-icon.svg"
onClick={() => handleNavigation('/customers')}
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Orders"
src="/orders-icon.svg"
onClick={() => handleNavigation('/employee/create-order')}
/>
<img
className="w-[25px] h-[25px] relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Account"
src="/account-icon.svg"
onClick={() => handleNavigation('/account')}
/>
</div>
</div>
</div>
<div className="flex flex-row items-start justify-start py-0 px-2">
<div className="flex flex-col items-start justify-start">
<img
className="w-6 h-6 relative overflow-hidden shrink-0 cursor-pointer"
loading="lazy"
alt="Logout"
src="/materialsymbolslogout.svg"
onClick={() => handleNavigation('/logout')}
/>
</div>
</div>
</div>
</div>
<div className="flex flex-col items-start justify-start pt-[46px] px-0 pb-0">
<div className="relative leading-[53px] lowercase font-semibold mq450:text-10xl mq450:leading-[32px] mq1025:text-19xl mq1025:leading-[42px]">
categories
</div>
</div>
<div className="w-[820px] flex flex-col items-start justify-start pt-[135px] px-0 pb-0 box-border max-w-full mq750:pt-[57px] mq750:box-border mq1025:pt-[88px] mq1025:box-border">
<div className="self-stretch h-[820px] overflow-y-auto shrink-0 flex flex-row flex-wrap items-start justify-start p-5 box-border relative gap-[80px_76px]">
<ProductRow image17="/image-17@2x.png" shirts="Shirts" />
<ProductRow
image17="/image-18@2x.png"
shirts="Suits"
propLeft="450px"
propTop="20px"
/>
<ProductRow
image17="/image-6@2x.png"
shirts="Tuxedos"
propLeft="20px"
propTop="450px"
/>
<ProductRow
image17="/image-28@2x.png"
shirts="jackets"
propLeft="450px"
propTop="450px"
/>
</div>
</div>
</div>
);
};
CategoryList.propTypes = {
className: PropTypes.string,
};
export default CategoryList;

View File

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

View File

View File

@ -69,12 +69,25 @@ const AddCustomer = () => {
fetchCities();
}, []);
// Updated handleInputChange function
const handleInputChange = (e) => {
const { name, value } = e.target;
if (name.startsWith('address.')) {
const addressField = name.split('.')[1];
setCustomerData((prevData) => ({
...prevData,
address: {
...prevData.address,
[addressField]: value,
},
}));
} else {
setCustomerData((prevData) => ({
...prevData,
[name]: value,
}));
}
};
const handleImageChange = (e) => {
@ -88,23 +101,34 @@ const AddCustomer = () => {
}
};
// Updated handleAddCustomerSubmit function
const handleAddCustomerSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
// Append flat fields
Object.keys(customerData).forEach((key) => {
if (key !== 'address' && key !== 'image') {
formData.append(key, customerData[key]);
}
});
// Append address fields
Object.keys(customerData.address).forEach((key) => {
formData.append(`address[${key}]`, customerData.address[key]);
});
// Append image file
if (customerData.image) {
formData.append('image', customerData.image);
}
try {
const response = await axiosInstance.post(
'/customers',
formData,
{
const response = await axiosInstance.post('/customers', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
}
);
});
console.log('Customer added successfully:', response.data);
navigate('/customers');
} catch (error) {
@ -157,7 +181,7 @@ const AddCustomer = () => {
}
className={CLASSES.uploadButton}
>
upload
add customer image
</button>
</div>
</div>
@ -171,110 +195,131 @@ const AddCustomer = () => {
</div>
</div>
<div className={CLASSES.formSection}>
<form onSubmit={handleAddCustomerSubmit}>
<div>
<label className={CLASSES.formGroup}>first name:</label>
<form onSubmit={handleAddCustomerSubmit} className="space-y-4">
<div className={CLASSES.formGroup}>
<label htmlFor="firstname">First Name</label>
<input
type="text"
id="firstname"
name="firstname"
className={CLASSES.formInput}
value={customerData.firstname}
onChange={handleInputChange}
className={CLASSES.formInput}
/>
</div>
<div>
<label className={CLASSES.formGroup}>last name:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="lastname">Last Name</label>
<input
type="text"
id="lastname"
name="lastname"
className={CLASSES.formInput}
value={customerData.lastname}
onChange={handleInputChange}
className={CLASSES.formInput}
/>
</div>
<div>
<label className={CLASSES.formGroup}>age:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="age">Age</label>
<input
type="number"
id="age"
name="age"
className={CLASSES.formInput}
value={customerData.age}
onChange={handleInputChange}
className={CLASSES.formInput}
/>
</div>
<div>
<label className={CLASSES.formGroup}>gender:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="gender">Gender</label>
<select
id="gender"
name="gender"
className={CLASSES.formSelect}
value={customerData.gender}
onChange={handleInputChange}
className={CLASSES.formSelect}
>
<option value="" disabled>select gender</option>
<option value="male">male</option>
<option value="female">female</option>
<option value="other">other</option>
<option value="">Select gender</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
</select>
</div>
<div>
<label className={CLASSES.formGroup}>email:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
className={CLASSES.formInput}
value={customerData.email}
onChange={handleInputChange}
className={CLASSES.formInput}
/>
</div>
<div>
<label className={CLASSES.formGroup}>address line 1:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="mobile">Mobile Number</label>
<input
type="text"
name="address_line_1"
value={customerData.address_line_1}
onChange={handleInputChange}
id="mobile"
name="mobile"
className={CLASSES.formInput}
value={customerData.mobile}
onChange={handleInputChange}
/>
</div>
<div>
<label className={CLASSES.formGroup}>address line 2:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="address_line_1">Address Line 1</label>
<input
type="text"
name="address_line_2"
value={customerData.address_line_2}
onChange={handleInputChange}
id="address_line_1"
name="address.address_line_1"
className={CLASSES.formInput}
value={customerData.address.address_line_1}
onChange={handleInputChange}
/>
</div>
<div>
<label className={CLASSES.formGroup}>nearby landmark:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="address_line_2">Address Line 2</label>
<input
type="text"
name="nearby_landmark"
value={customerData.nearby_landmark}
onChange={handleInputChange}
id="address_line_2"
name="address.address_line_2"
className={CLASSES.formInput}
value={customerData.address.address_line_2}
onChange={handleInputChange}
/>
</div>
<div>
<label className={CLASSES.formGroup}>pincode:</label>
<div className={CLASSES.formGroup}>
<label htmlFor="nearby_landmark">Nearby Landmark</label>
<input
type="text"
name="pincode"
value={customerData.pincode}
onChange={handleInputChange}
id="nearby_landmark"
name="address.nearby_landmark"
className={CLASSES.formInput}
value={customerData.address.nearby_landmark}
onChange={handleInputChange}
/>
</div>
<div>
<label className={CLASSES.formGroup}>city:</label>
<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
name="city_id"
value={customerData.city_id}
onChange={handleInputChange}
id="city_id"
name="address.city_id"
className={CLASSES.formSelect}
value={customerData.address.city_id}
onChange={handleInputChange}
>
<option value="" disabled>select city</option>
<option value="">Select City</option>
{cities.map((city) => (
<option key={city.id} value={city.id}>
{city.name}
@ -282,19 +327,11 @@ const AddCustomer = () => {
))}
</select>
</div>
<div>
<label className={CLASSES.formGroup}>mobile:</label>
<input
type="text"
name="mobile"
value={customerData.mobile}
onChange={handleInputChange}
className={CLASSES.formInput}
/>
</div>
<div className={CLASSES.formGroup}>
<button type="submit" className={CLASSES.submitButton}>
add customer
save customer
</button>
</div>
</form>
</div>
</div>

View File

@ -5,7 +5,6 @@ import axios from 'axios';
import Sidebar from '../sidebar/Sidebar';
import '../../styles/CustomerMeasurements.css';
const CustomerMeasurements = () => {
const onFinish = async (values) => {
const token = localStorage.getItem('token'); // Assuming the token is stored in localStorage
@ -24,54 +23,54 @@ const CustomerMeasurements = () => {
};
return (
<div className="container">
<div className="container flex">
<Sidebar />
<div className="content">
<div className="form-container">
<div className="header">
<div className="content flex-1">
<div className="form-container max-w-2xl mx-auto p-6">
<div className="header flex justify-between items-center mb-4">
<h1 className="text-2xl font-semibold text-[#0e355b]">Customer Measurements</h1>
<Link to="/CategoryList" className="skip-link ">Skip for now</Link>
</div>
<Form onFinish={onFinish} className="form">
<h2 className="section-title">Upper Body Measurements</h2>
<Form onFinish={onFinish} className="form space-y-4">
<h2 className="section-title text-lg font-medium">Upper Body Measurements</h2>
<Form.Item name="neck" label="Neck Circumference (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="chest" label="Chest Circumference (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="waist" label="Waist Circumference (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="shoulder" label="Shoulder Width (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="arm" label="Arm Length (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="sleeve" label="Sleeve Length (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<h2 className="section-title">Lower Body Measurements</h2>
<h2 className="section-title text-lg font-medium">Lower Body Measurements</h2>
<Form.Item name="hip" label="Hip Circumference (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="inseam" label="Inseam (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="outseam" label="Outseam (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="thigh" label="Thigh Circumference (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item name="ankle" label="Ankle Circumference (in inch)">
<Input className="input" />
<Input className="input focus:outline-none focus:ring-0 border-b border-gray-400" />
</Form.Item>
<Form.Item>
<div className="button-container">
<Button type="primary" htmlType="submit" className="submit-button">
<div className="button-container flex justify-end">
<Button type="primary" htmlType="submit" className="submit-button bg-[#0e355b] text-white py-2 px-4 rounded-md">
Save Measurements
</Button>
</div>

View File

@ -14,7 +14,7 @@ const { Sider } = Layout;
const classNames = {
sider: 'desktop-sidebar',
container: 'flex flex-col items-center mt-4',
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',

View File

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

View File

@ -1,79 +1,74 @@
/* Container styles */
/* CustomerMeasurements.css */
/* Container styling */
.container {
display: flex;
flex-direction: row;
}
/* Content styles */
/* Content styling */
.content {
width: 100%;
display: flex;
flex-direction: column;
min-height: 100vh;
color: #ffffff;
flex: 1;
}
/* Form container styles */
/* Form container styling */
.form-container {
min-height: 100vh;
color: #4b5563;
padding: 1.5rem;
max-width: 40rem; /* 640px */
margin: 0 auto;
padding: 1.5rem; /* 24px */
}
/* Header styles */
/* Header styling */
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.title {
font-size: 2rem;
font-weight: 600;
color: #0e355b;
margin-bottom: 1rem; /* 16px */
}
/* Skip link styling */
.skip-link {
color: #0e355b;
}
/* Form styles */
.form {
display: flex;
flex-direction: column;
gap: 1rem;
color: #0e355b; /* Tailwind's blue-600 */
text-decoration: none;
}
/* Section title styling */
.section-title {
font-size: 1.125rem;
font-weight: 600;
color: #0e355b;
font-size: 1.125rem; /* 18px */
font-weight: 500;
margin-bottom: 0.5rem; /* 8px */
}
/* Input styles */
/* Input field styling */
.input {
width: 100%;
border: 0;
border-bottom: 1px solid #d1d5db;
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-color: #4b5563;
box-shadow: none;
border-bottom-color: #0e355b; /* Custom blue color for focus */
}
/* Button container styles */
/* Button container styling */
.button-container {
text-align: center;
display: flex;
justify-content: flex-end;
}
/* Submit button styles */
/* Submit button styling */
.submit-button {
background-color: #0e355b;
color: #d1d5db;
padding: 0.5rem 1rem;
border-radius: 0.375rem;
font-size: 0.875rem;
color: white;
padding: 0.5rem 1rem; /* 8px top and bottom, 16px left and right */
border-radius: 0.375rem; /* 6px */
border: none;
cursor: pointer;
transition: background-color 0.3s ease;
}
.submit-button:hover {
background-color: #0e355b; /* Darker shade for hover */
}

View File

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

View File

@ -6,7 +6,7 @@ export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': '/src',
'@api': '/src/api',
},
},
});

9754
yarn.lock

File diff suppressed because it is too large Load Diff