retailers edit option there where anything can be editable
This commit is contained in:
parent
9e25d6f37b
commit
7a0bd7a228
@ -81,6 +81,7 @@
|
|||||||
"sweetalert": "^2.1.2",
|
"sweetalert": "^2.1.2",
|
||||||
"sweetalert2": "^11.4.0",
|
"sweetalert2": "^11.4.0",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
|
"validator": "^13.12.0",
|
||||||
"xlsx": "^0.18.5"
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -170,6 +170,7 @@ import UploadOpeningInventory from "./views/PrincipalDistributors/UploadOpeningI
|
|||||||
import OpeningInventoryReports from "./views/Reports/OpeningInventoryReports";
|
import OpeningInventoryReports from "./views/Reports/OpeningInventoryReports";
|
||||||
import StockReports from "./views/Reports/StockReports ";
|
import StockReports from "./views/Reports/StockReports ";
|
||||||
import Transporter from "./views/Transporter/Transporter";
|
import Transporter from "./views/Transporter/Transporter";
|
||||||
|
import EditRetailDistributor from "./views/RetailDistributors/EditRetailDistributor";
|
||||||
const routes = [
|
const routes = [
|
||||||
//dashboard
|
//dashboard
|
||||||
|
|
||||||
@ -398,6 +399,12 @@ const routes = [
|
|||||||
element: AddRetailDistributor,
|
element: AddRetailDistributor,
|
||||||
navName: "RetailDistributor",
|
navName: "RetailDistributor",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/retaildistributor/edit/:id",
|
||||||
|
name: "Edit Retailer",
|
||||||
|
element: EditRetailDistributor,
|
||||||
|
navName: "RetailDistributor",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "/add-retail-distributor/multiple",
|
path: "/add-retail-distributor/multiple",
|
||||||
name: "Add Retailer",
|
name: "Add Retailer",
|
||||||
|
795
src/views/RetailDistributors/EditRetailDistributor.js
Normal file
795
src/views/RetailDistributors/EditRetailDistributor.js
Normal file
@ -0,0 +1,795 @@
|
|||||||
|
import React, { useState, useEffect, useCallback } from "react";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
CircularProgress,
|
||||||
|
Card,
|
||||||
|
Button,
|
||||||
|
Typography,
|
||||||
|
Grid,
|
||||||
|
Paper,
|
||||||
|
Avatar,
|
||||||
|
IconButton,
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Autocomplete,
|
||||||
|
TextField,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { useNavigate, useParams } from "react-router-dom";
|
||||||
|
import { toast } from "react-hot-toast";
|
||||||
|
import axios from "axios";
|
||||||
|
import { isAutheticated } from "src/auth";
|
||||||
|
import { City, State } from "country-state-city";
|
||||||
|
import CancelIcon from "@mui/icons-material/Cancel";
|
||||||
|
import DeleteIcon from "@mui/icons-material/Delete";
|
||||||
|
import validator from "validator";
|
||||||
|
|
||||||
|
const EditRetailDistributor = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const token = isAutheticated();
|
||||||
|
const { id } = useParams();
|
||||||
|
const [kycid, setKycid] = useState("");
|
||||||
|
const [user, setUser] = useState({
|
||||||
|
name: "",
|
||||||
|
email: "",
|
||||||
|
trade_name: "",
|
||||||
|
address: "",
|
||||||
|
state: "",
|
||||||
|
city: "",
|
||||||
|
district: "",
|
||||||
|
pincode: "",
|
||||||
|
mobile_number: "",
|
||||||
|
pan_number: "",
|
||||||
|
aadhar_number: "",
|
||||||
|
gst_number: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [stateOptions, setStateOptions] = useState([]);
|
||||||
|
const [cityOptions, setCityOptions] = useState([]);
|
||||||
|
const [selectedState, setSelectedState] = useState(null);
|
||||||
|
const [selectedCity, setSelectedCity] = useState(null);
|
||||||
|
const [errors, setErrors] = useState({});
|
||||||
|
|
||||||
|
// Fetch all available states on mount
|
||||||
|
useEffect(() => {
|
||||||
|
const states = State.getStatesOfCountry("IN").map((state) => ({
|
||||||
|
label: state.name,
|
||||||
|
value: state.isoCode,
|
||||||
|
}));
|
||||||
|
setStateOptions(states);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Fetch city options whenever selected state changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedState) {
|
||||||
|
const cities = City.getCitiesOfState("IN", selectedState.value).map(
|
||||||
|
(city) => ({
|
||||||
|
label: city.name,
|
||||||
|
value: city.name,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setCityOptions(cities);
|
||||||
|
} else {
|
||||||
|
setCityOptions([]); // Reset cities if no state selected
|
||||||
|
}
|
||||||
|
}, [selectedState]);
|
||||||
|
|
||||||
|
const handleInputChange = (e) => {
|
||||||
|
setUser({ ...user, [e.target.name]: e.target.value });
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStateChange = (event, newValue) => {
|
||||||
|
setSelectedState(newValue);
|
||||||
|
setUser((prev) => ({
|
||||||
|
...prev,
|
||||||
|
state: newValue ? newValue.label : "",
|
||||||
|
city: "",
|
||||||
|
}));
|
||||||
|
setSelectedCity(null); // Clear city when state changes
|
||||||
|
setCityOptions([]); // Reset city options
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCityChange = (event, newValue) => {
|
||||||
|
setSelectedCity(newValue);
|
||||||
|
setUser((prev) => ({
|
||||||
|
...prev,
|
||||||
|
city: newValue ? newValue.label : "",
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
// Validation function
|
||||||
|
const validateFields = () => {
|
||||||
|
const validationErrors = [];
|
||||||
|
|
||||||
|
if (!user.name) validationErrors.push("Name is required.");
|
||||||
|
if (!user.email || !validator.isEmail(user.email)) {
|
||||||
|
validationErrors.push("Invalid or missing email.");
|
||||||
|
}
|
||||||
|
if (!user.trade_name) validationErrors.push("Trade name is required.");
|
||||||
|
if (!user.address) validationErrors.push("Address is required.");
|
||||||
|
if (!user.mobile_number || !/^\d{10}$/.test(user.mobile_number)) {
|
||||||
|
validationErrors.push("Invalid mobile number (should be 10 digits).");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!user.pan_number ||
|
||||||
|
!/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/.test(user.pan_number)
|
||||||
|
) {
|
||||||
|
validationErrors.push("Invalid PAN number.");
|
||||||
|
}
|
||||||
|
if (!user.aadhar_number || !/^\d{12}$/.test(user.aadhar_number)) {
|
||||||
|
validationErrors.push("Invalid Aadhar number (should be 12 digits).");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!user.gst_number ||
|
||||||
|
!/^(\d{2}[A-Z]{5}\d{4}[A-Z]{1}\d[Z]{1}[A-Z\d]{1})$/.test(user.gst_number)
|
||||||
|
) {
|
||||||
|
validationErrors.push("Invalid GST number.");
|
||||||
|
}
|
||||||
|
if (!user.state) validationErrors.push("State is required.");
|
||||||
|
if (!user.city) validationErrors.push("City is required.");
|
||||||
|
if (!user.pincode || !/^\d{6}$/.test(user.pincode)) {
|
||||||
|
validationErrors.push("Invalid Postal Code.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return validationErrors;
|
||||||
|
};
|
||||||
|
const handleFormSubmit = async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
// Validate required fields
|
||||||
|
const validationErrors = validateFields();
|
||||||
|
|
||||||
|
if (validationErrors.length > 0) {
|
||||||
|
// Display each validation error in a toast message
|
||||||
|
validationErrors.forEach((error) => toast.error(error));
|
||||||
|
setErrors(validationErrors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const formData = new FormData();
|
||||||
|
Object.keys(user).forEach((key) => formData.append(key, user[key]));
|
||||||
|
|
||||||
|
// Append files only if they are modified
|
||||||
|
// Object.keys(files).forEach((key) => {
|
||||||
|
// if (files[key] && !(typeof files[key] === "string")) {
|
||||||
|
// formData.append(key, files[key]);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// Only include changed images in the formData
|
||||||
|
Object.entries(files).forEach(([key, value]) => {
|
||||||
|
if (value?.file) {
|
||||||
|
formData.append(key, value.file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.put(
|
||||||
|
`/api/retailer/update-admin/${id}`,
|
||||||
|
formData,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (response.data.success) {
|
||||||
|
toast.success("Retailer updated successfully!");
|
||||||
|
navigate("/retail-distributor");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error.response?.data?.message || "Something went wrong!");
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Helper function to format names
|
||||||
|
const formatName = (Name) => {
|
||||||
|
return Name.toLowerCase()
|
||||||
|
.split(" ")
|
||||||
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
||||||
|
.join(" ");
|
||||||
|
};
|
||||||
|
const getUserDetails = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`/api/getRD/${id}`, {
|
||||||
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
|
});
|
||||||
|
const userData = response.data;
|
||||||
|
// console.log(userData);
|
||||||
|
setKycid(userData.kyc._id);
|
||||||
|
if (userData?.kyc) {
|
||||||
|
const formattedState = formatName(userData?.kyc?.state);
|
||||||
|
const stateOption =
|
||||||
|
stateOptions.find((s) => s.label === formattedState) || null;
|
||||||
|
setSelectedState(stateOption);
|
||||||
|
let formattedCity = formatName(userData.kyc.city);
|
||||||
|
if (stateOption) {
|
||||||
|
const cities = City.getCitiesOfState("IN", stateOption.value).map(
|
||||||
|
(city) => ({
|
||||||
|
label: city.name,
|
||||||
|
value: city.name,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setCityOptions(cities);
|
||||||
|
const cityOption =
|
||||||
|
cities.find((c) => c.label === formattedCity) || null;
|
||||||
|
setSelectedCity(cityOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
setUser({
|
||||||
|
name: userData.name,
|
||||||
|
email: userData.email,
|
||||||
|
trade_name: userData.kyc.trade_name,
|
||||||
|
address: userData.kyc.address,
|
||||||
|
district: userData.kyc.district,
|
||||||
|
pincode: userData.kyc.pincode,
|
||||||
|
mobile_number: userData.kyc.mobile_number,
|
||||||
|
pan_number: userData.kyc.pan_number,
|
||||||
|
aadhar_number: userData.kyc.aadhar_number,
|
||||||
|
gst_number: userData.kyc.gst_number,
|
||||||
|
state: formattedState ? formattedState : userData.kyc.state,
|
||||||
|
city: formattedCity ? formattedCity : userData.kyc.city,
|
||||||
|
});
|
||||||
|
setFiles({
|
||||||
|
selfieEntranceImg: userData.kyc?.selfie_entrance_img,
|
||||||
|
panImg: userData.kyc?.pan_img,
|
||||||
|
aadharImg: userData.kyc?.aadhar_img,
|
||||||
|
gstImg: userData.kyc?.gst_img,
|
||||||
|
pesticideLicenseImg: userData.kyc?.pesticide_license_img,
|
||||||
|
fertilizerLicenseImg: userData.kyc?.fertilizer_license_img,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching user details:", error);
|
||||||
|
}
|
||||||
|
}, [id, token, stateOptions]);
|
||||||
|
|
||||||
|
// Fetch retailer details on component mount
|
||||||
|
useEffect(() => {
|
||||||
|
getUserDetails();
|
||||||
|
}, [getUserDetails]);
|
||||||
|
|
||||||
|
const [files, setFiles] = useState({
|
||||||
|
selfieEntranceImg: null,
|
||||||
|
panImg: null,
|
||||||
|
aadharImg: null,
|
||||||
|
gstImg: null,
|
||||||
|
pesticideLicenseImg: null,
|
||||||
|
fertilizerLicenseImg: null,
|
||||||
|
});
|
||||||
|
const [openPopup, setOpenPopup] = useState(false);
|
||||||
|
const [selectedImage, setSelectedImage] = useState("");
|
||||||
|
|
||||||
|
const handleOpenPopup = (imageUrl) => {
|
||||||
|
setSelectedImage(imageUrl);
|
||||||
|
setOpenPopup(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClosePopup = () => {
|
||||||
|
setOpenPopup(false);
|
||||||
|
setSelectedImage("");
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileChange = (e) => {
|
||||||
|
const { name, files } = e.target;
|
||||||
|
if (files.length > 0) {
|
||||||
|
const file = files[0];
|
||||||
|
if (["image/png", "image/jpeg", "image/jpg"].includes(file.type)) {
|
||||||
|
setFiles((prev) => ({ ...prev, [name]: { file } }));
|
||||||
|
} else {
|
||||||
|
alert("Only PNG, JPG, and JPEG files are allowed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// console.log(kycid);
|
||||||
|
const handleDeleteImage = async (name) => {
|
||||||
|
if (files[name]?.url) {
|
||||||
|
// Backend deletion
|
||||||
|
try {
|
||||||
|
await axios({
|
||||||
|
method: "delete",
|
||||||
|
url: `/api/deleteImage/${files[name].public_id}`, // Specify the image public_id to delete
|
||||||
|
data: {
|
||||||
|
kycid: kycid,
|
||||||
|
imageType: name,
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${token}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
setFiles((prev) => ({ ...prev, [name]: null }));
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error deleting image:", error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Local deletion
|
||||||
|
setFiles((prev) => ({ ...prev, [name]: null }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderImageWithDelete = (name, image) => (
|
||||||
|
<div style={{ position: "relative", width: 100, height: 100 }}>
|
||||||
|
<Avatar
|
||||||
|
variant="square"
|
||||||
|
src={image.url || URL.createObjectURL(image.file)}
|
||||||
|
sx={{ width: "100%", height: "100%", cursor: "pointer" }}
|
||||||
|
onClick={() =>
|
||||||
|
handleOpenPopup(image.url || URL.createObjectURL(image.file))
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
sx={{ position: "absolute", top: -10, right: -10, color: "red" }}
|
||||||
|
onClick={() => handleDeleteImage(name)}
|
||||||
|
>
|
||||||
|
<DeleteIcon />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const handleCancel = () => {
|
||||||
|
navigate("/retail-distributor");
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Card
|
||||||
|
sx={{ padding: "1rem", marginBottom: "1rem", position: "relative" }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
onClick={handleCancel}
|
||||||
|
sx={{ position: "absolute", top: "10px", right: "10px" }}
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Typography variant="h5" sx={{ mb: 3 }}>
|
||||||
|
Edit Retailer
|
||||||
|
</Typography>
|
||||||
|
<form onSubmit={handleFormSubmit}>
|
||||||
|
<Typography variant="h5" sx={{ mb: 2 }}>
|
||||||
|
Basic Information
|
||||||
|
</Typography>
|
||||||
|
<Grid container spacing={2} sx={{ mb: 2 }}>
|
||||||
|
{/* Name */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="name"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
Name*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="name"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="name"
|
||||||
|
value={user.name}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="email"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
Email*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="email"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="email"
|
||||||
|
value={user.email}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
{/* Trade Name */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="trade_name"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
Trade Name*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="trade_name"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="trade_name"
|
||||||
|
value={user.trade_name}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Address */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="address"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
Address*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="address"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="address"
|
||||||
|
value={user.address}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Mobile Number */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="mobile_number"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
Mobile Number*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="mobile_number"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="mobile_number"
|
||||||
|
value={user.mobile_number}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* PAN Number */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="pan_number"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
PAN Number*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="pan_number"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="pan_number"
|
||||||
|
value={user.pan_number}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Aadhar Number */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="aadhar_number"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
Aadhar Number*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="aadhar_number"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="aadhar_number"
|
||||||
|
value={user.aadhar_number}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* GST Number */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="gst_number"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
GST Number*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="gst_number"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="gst_number"
|
||||||
|
value={user.gst_number}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* State */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="state"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
State*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={5}>
|
||||||
|
<Autocomplete
|
||||||
|
options={stateOptions}
|
||||||
|
value={selectedState}
|
||||||
|
onChange={handleStateChange}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField {...params} label="Select State" />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={5}>
|
||||||
|
<TextField
|
||||||
|
id="State"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="State"
|
||||||
|
value={user.state}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* City */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="city"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
City*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={5}>
|
||||||
|
<Autocomplete
|
||||||
|
options={cityOptions}
|
||||||
|
value={selectedCity}
|
||||||
|
onChange={handleCityChange}
|
||||||
|
isOptionEqualToValue={(option, value) =>
|
||||||
|
option.value === value.value
|
||||||
|
}
|
||||||
|
renderInput={(params) => (
|
||||||
|
<TextField {...params} label="Select City" />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={5}>
|
||||||
|
<TextField
|
||||||
|
id="City"
|
||||||
|
required
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="City"
|
||||||
|
value={user.city}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* District */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="district"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
District*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="district"
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="district"
|
||||||
|
value={user.district}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Pincode */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}>
|
||||||
|
<Typography
|
||||||
|
variant="body1"
|
||||||
|
htmlFor="pincode"
|
||||||
|
className="form-label"
|
||||||
|
>
|
||||||
|
Pincode*
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<TextField
|
||||||
|
id="pincode"
|
||||||
|
type="text"
|
||||||
|
fullWidth
|
||||||
|
name="pincode"
|
||||||
|
value={user.pincode}
|
||||||
|
variant="outlined"
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* File Uploads */}
|
||||||
|
<Paper sx={{ p: 4, mb: 3, width: "100%" }}>
|
||||||
|
<Typography variant="h6" gutterBottom textAlign="center">
|
||||||
|
Upload Documents
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Grid container spacing={3}>
|
||||||
|
{Object.entries({
|
||||||
|
panImg: "PAN Image",
|
||||||
|
aadharImg: "Aadhar Image",
|
||||||
|
gstImg: "GST Image",
|
||||||
|
pesticideLicenseImg: "Pesticide License",
|
||||||
|
fertilizerLicenseImg: "Fertilizer License",
|
||||||
|
selfieEntranceImg: "Selfie of Entrance Board",
|
||||||
|
}).map(([name, label]) => (
|
||||||
|
<Grid item xs={12} sm={6} key={name}>
|
||||||
|
<Card
|
||||||
|
sx={{
|
||||||
|
p: 2,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "center",
|
||||||
|
border: "1px solid #ddd",
|
||||||
|
borderRadius: "8px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Typography
|
||||||
|
variant="body2"
|
||||||
|
textAlign="center"
|
||||||
|
sx={{ mb: 1 }}
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
{files[name] ? (
|
||||||
|
renderImageWithDelete(name, files[name])
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
component="label"
|
||||||
|
sx={{ mt: 1, width: "100%" }}
|
||||||
|
>
|
||||||
|
Upload
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id={name}
|
||||||
|
name={name}
|
||||||
|
onChange={handleFileChange}
|
||||||
|
accept=".png,.jpg,.jpeg"
|
||||||
|
hidden
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
{/* Image Popup */}
|
||||||
|
<Dialog
|
||||||
|
open={openPopup}
|
||||||
|
onClose={handleClosePopup}
|
||||||
|
maxWidth="md"
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
<DialogTitle>
|
||||||
|
<IconButton
|
||||||
|
sx={{
|
||||||
|
position: "absolute",
|
||||||
|
right: 16,
|
||||||
|
top: 16,
|
||||||
|
color: "red",
|
||||||
|
}}
|
||||||
|
onClick={handleClosePopup}
|
||||||
|
>
|
||||||
|
<CancelIcon />
|
||||||
|
</IconButton>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
p: 0,
|
||||||
|
overflow: "auto",
|
||||||
|
maxHeight: "80vh",
|
||||||
|
maxWidth: "80vw",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={selectedImage}
|
||||||
|
alt="Large Preview"
|
||||||
|
style={{
|
||||||
|
maxWidth: "100%",
|
||||||
|
maxHeight: "100%",
|
||||||
|
objectFit: "contain",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</Paper>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<Grid item xs={12} className="d-flex align-items-center">
|
||||||
|
<Grid item xs={2}></Grid>
|
||||||
|
<Grid item xs={10}>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={loading}
|
||||||
|
>
|
||||||
|
{loading ? <CircularProgress size={24} /> : "Submit"}
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</form>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditRetailDistributor;
|
@ -166,15 +166,15 @@ const RetailDistributor = () => {
|
|||||||
style={{ background: "#ecdddd" }}
|
style={{ background: "#ecdddd" }}
|
||||||
>
|
>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th style={{ width: "10%" }}>ID</th>
|
||||||
<th className="text-start">Trade Name</th>
|
<th style={{ width: "12%" }}>Trade Name</th>
|
||||||
<th className="text-start">Created On</th>
|
<th style={{ width: "10%" }}>Created On</th>
|
||||||
<th className="text-start">Principal Distributor</th>
|
<th style={{ width: "14%" }}>Principal Distributor</th>
|
||||||
<th className="text-start">Territory Manager</th>
|
<th style={{ width: "14%" }}>Territory Manager</th>
|
||||||
<th className="text-start">Sales Coordinator</th>
|
<th style={{ width: "14%" }}>Sales Coordinator</th>
|
||||||
<th className="text-start">Orders</th>
|
<th style={{ width: "6%" }}>Orders</th>
|
||||||
<th className="text-start">Mapping</th>
|
<th style={{ width: "6%" }}>Mapping</th>
|
||||||
<th className="text-center">Action</th>
|
<th style={{ width: "11%" }}>Action</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ const RetailDistributor = () => {
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className=" btn btn-info btn-sm waves-effect waves-light btn-table ml-2"
|
className=" btn btn-info btn-sm waves-effect waves-light btn-table"
|
||||||
>
|
>
|
||||||
View
|
View
|
||||||
</button>
|
</button>
|
||||||
@ -287,13 +287,21 @@ const RetailDistributor = () => {
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className=" btn btn-info btn-sm waves-effect waves-light btn-table ml-1
|
className=" btn btn-info btn-sm waves-effect waves-light btn-table mt-1"
|
||||||
md-mt-1
|
|
||||||
md-ml-0"
|
|
||||||
>
|
>
|
||||||
OI
|
OI
|
||||||
</button>
|
</button>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link
|
||||||
|
to={`/retaildistributor/edit/${retailDistributor._id}`}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className=" btn btn-info btn-sm waves-effect waves-light btn-table mt-1 ml-1"
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))
|
))
|
||||||
|
@ -13,6 +13,7 @@ import { toast } from "react-hot-toast";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { isAutheticated } from "src/auth";
|
import { isAutheticated } from "src/auth";
|
||||||
import { City, State } from "country-state-city";
|
import { City, State } from "country-state-city";
|
||||||
|
import validator from "validator";
|
||||||
|
|
||||||
const AddRetailDistributor = () => {
|
const AddRetailDistributor = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -98,27 +99,54 @@ const AddRetailDistributor = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Validation function
|
||||||
|
const validateFields = () => {
|
||||||
|
const validationErrors = [];
|
||||||
|
|
||||||
|
if (!user.name) validationErrors.push("Name is required.");
|
||||||
|
if (!user.email || !validator.isEmail(user.email)) {
|
||||||
|
validationErrors.push("Invalid or missing email.");
|
||||||
|
}
|
||||||
|
if (!user.trade_name) validationErrors.push("Trade name is required.");
|
||||||
|
if (!user.address) validationErrors.push("Address is required.");
|
||||||
|
if (!user.mobile_number || !/^\d{10}$/.test(user.mobile_number)) {
|
||||||
|
validationErrors.push("Invalid mobile number (should be 10 digits).");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!user.pan_number ||
|
||||||
|
!/^[A-Z]{5}[0-9]{4}[A-Z]{1}$/.test(user.pan_number)
|
||||||
|
) {
|
||||||
|
validationErrors.push("Invalid PAN number.");
|
||||||
|
}
|
||||||
|
if (!user.aadhar_number || !/^\d{12}$/.test(user.aadhar_number)) {
|
||||||
|
validationErrors.push("Invalid Aadhar number (should be 12 digits).");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
!user.gst_number ||
|
||||||
|
!/^(\d{2}[A-Z]{5}\d{4}[A-Z]{1}\d[Z]{1}[A-Z\d]{1})$/.test(user.gst_number)
|
||||||
|
) {
|
||||||
|
validationErrors.push("Invalid GST number.");
|
||||||
|
}
|
||||||
|
if (!user.state) validationErrors.push("State is required.");
|
||||||
|
if (!user.city) validationErrors.push("City is required.");
|
||||||
|
if (!user.pincode || !/^\d{6}$/.test(user.pincode)) {
|
||||||
|
validationErrors.push("Invalid Postal Code.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return validationErrors;
|
||||||
|
};
|
||||||
const handleFormSubmit = async (e) => {
|
const handleFormSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
// Validate input fields
|
// Validate required fields
|
||||||
if (
|
const validationErrors = validateFields();
|
||||||
!user.name ||
|
|
||||||
!user.email ||
|
if (validationErrors.length > 0) {
|
||||||
!user.trade_name ||
|
// Display each validation error in a toast message
|
||||||
!user.address ||
|
validationErrors.forEach((error) => toast.error(error));
|
||||||
!user.mobile_number ||
|
setErrors(validationErrors);
|
||||||
!user.pan_number ||
|
|
||||||
!user.aadhar_number ||
|
|
||||||
!user.gst_number ||
|
|
||||||
!selectedState ||
|
|
||||||
!selectedCity
|
|
||||||
) {
|
|
||||||
setErrors({ message: "Fill all required fields!" });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@ -146,7 +174,7 @@ const AddRetailDistributor = () => {
|
|||||||
const response = await axios.post("/api/kyc/create-admin/", formData, {
|
const response = await axios.post("/api/kyc/create-admin/", formData, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "multipart/form-data",
|
"Content-Type": "multipart/form-data",
|
||||||
"Authorization": `Bearer ${token}`,
|
Authorization: `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -495,10 +523,21 @@ const AddRetailDistributor = () => {
|
|||||||
{ label: "PAN Image*", name: "panImg" },
|
{ label: "PAN Image*", name: "panImg" },
|
||||||
{ label: "Aadhar Image*", name: "aadharImg" },
|
{ label: "Aadhar Image*", name: "aadharImg" },
|
||||||
{ label: "GST Image*", name: "gstImg" },
|
{ label: "GST Image*", name: "gstImg" },
|
||||||
{ label: "Pesticide License Image*", name: "pesticideLicenseImg" },
|
{
|
||||||
{ label: "Fertilizer License Image*", name: "fertilizerLicenseImg" },
|
label: "Pesticide License Image*",
|
||||||
|
name: "pesticideLicenseImg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Fertilizer License Image*",
|
||||||
|
name: "fertilizerLicenseImg",
|
||||||
|
},
|
||||||
].map(({ label, name }) => (
|
].map(({ label, name }) => (
|
||||||
<Grid item xs={12} className="d-flex align-items-center" key={name}>
|
<Grid
|
||||||
|
item
|
||||||
|
xs={12}
|
||||||
|
className="d-flex align-items-center"
|
||||||
|
key={name}
|
||||||
|
>
|
||||||
<Grid item xs={2}>
|
<Grid item xs={2}>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body1"
|
variant="body1"
|
||||||
@ -530,11 +569,7 @@ const AddRetailDistributor = () => {
|
|||||||
color="primary"
|
color="primary"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{loading ? <CircularProgress size={24} /> : "Submit"}
|
||||||
<CircularProgress size={24} />
|
|
||||||
) : (
|
|
||||||
"Submit"
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
@ -303,7 +303,7 @@ const TerritoryManager = () => {
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary btn-sm waves-effect waves-light btn-table ml-1 md-mt-1 md-ml-0"
|
className="btn btn-primary btn-sm waves-effect waves-light btn-table ml-1 md:mt-1 md:ml-0"
|
||||||
>
|
>
|
||||||
PD
|
PD
|
||||||
</button>
|
</button>
|
||||||
@ -313,7 +313,7 @@ const TerritoryManager = () => {
|
|||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-primary btn-sm waves-effect waves-light btn-table ml-1 md-mt-1 md-ml-0"
|
className="btn btn-primary btn-sm waves-effect waves-light btn-table ml-1 md:mt-1 md-ml-0"
|
||||||
>
|
>
|
||||||
Retailer
|
Retailer
|
||||||
</button>
|
</button>
|
||||||
|
Loading…
Reference in New Issue
Block a user