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",
|
||||
"sweetalert2": "^11.4.0",
|
||||
"uuid": "^9.0.1",
|
||||
"validator": "^13.12.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -170,6 +170,7 @@ import UploadOpeningInventory from "./views/PrincipalDistributors/UploadOpeningI
|
||||
import OpeningInventoryReports from "./views/Reports/OpeningInventoryReports";
|
||||
import StockReports from "./views/Reports/StockReports ";
|
||||
import Transporter from "./views/Transporter/Transporter";
|
||||
import EditRetailDistributor from "./views/RetailDistributors/EditRetailDistributor";
|
||||
const routes = [
|
||||
//dashboard
|
||||
|
||||
@ -398,6 +399,12 @@ const routes = [
|
||||
element: AddRetailDistributor,
|
||||
navName: "RetailDistributor",
|
||||
},
|
||||
{
|
||||
path: "/retaildistributor/edit/:id",
|
||||
name: "Edit Retailer",
|
||||
element: EditRetailDistributor,
|
||||
navName: "RetailDistributor",
|
||||
},
|
||||
{
|
||||
path: "/add-retail-distributor/multiple",
|
||||
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" }}
|
||||
>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th className="text-start">Trade Name</th>
|
||||
<th className="text-start">Created On</th>
|
||||
<th className="text-start">Principal Distributor</th>
|
||||
<th className="text-start">Territory Manager</th>
|
||||
<th className="text-start">Sales Coordinator</th>
|
||||
<th className="text-start">Orders</th>
|
||||
<th className="text-start">Mapping</th>
|
||||
<th className="text-center">Action</th>
|
||||
<th style={{ width: "10%" }}>ID</th>
|
||||
<th style={{ width: "12%" }}>Trade Name</th>
|
||||
<th style={{ width: "10%" }}>Created On</th>
|
||||
<th style={{ width: "14%" }}>Principal Distributor</th>
|
||||
<th style={{ width: "14%" }}>Territory Manager</th>
|
||||
<th style={{ width: "14%" }}>Sales Coordinator</th>
|
||||
<th style={{ width: "6%" }}>Orders</th>
|
||||
<th style={{ width: "6%" }}>Mapping</th>
|
||||
<th style={{ width: "11%" }}>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@ -258,7 +258,7 @@ const RetailDistributor = () => {
|
||||
>
|
||||
<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
|
||||
</button>
|
||||
@ -287,13 +287,21 @@ const RetailDistributor = () => {
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className=" btn btn-info btn-sm waves-effect waves-light btn-table ml-1
|
||||
md-mt-1
|
||||
md-ml-0"
|
||||
className=" btn btn-info btn-sm waves-effect waves-light btn-table mt-1"
|
||||
>
|
||||
OI
|
||||
</button>
|
||||
</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>
|
||||
</tr>
|
||||
))
|
||||
|
@ -13,6 +13,7 @@ import { toast } from "react-hot-toast";
|
||||
import axios from "axios";
|
||||
import { isAutheticated } from "src/auth";
|
||||
import { City, State } from "country-state-city";
|
||||
import validator from "validator";
|
||||
|
||||
const AddRetailDistributor = () => {
|
||||
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) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
// Validate input fields
|
||||
if (
|
||||
!user.name ||
|
||||
!user.email ||
|
||||
!user.trade_name ||
|
||||
!user.address ||
|
||||
!user.mobile_number ||
|
||||
!user.pan_number ||
|
||||
!user.aadhar_number ||
|
||||
!user.gst_number ||
|
||||
!selectedState ||
|
||||
!selectedCity
|
||||
) {
|
||||
setErrors({ message: "Fill all required fields!" });
|
||||
// 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();
|
||||
@ -146,7 +174,7 @@ const AddRetailDistributor = () => {
|
||||
const response = await axios.post("/api/kyc/create-admin/", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
"Authorization": `Bearer ${token}`,
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
@ -495,10 +523,21 @@ const AddRetailDistributor = () => {
|
||||
{ label: "PAN Image*", name: "panImg" },
|
||||
{ label: "Aadhar Image*", name: "aadharImg" },
|
||||
{ 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 }) => (
|
||||
<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}>
|
||||
<Typography
|
||||
variant="body1"
|
||||
@ -530,11 +569,7 @@ const AddRetailDistributor = () => {
|
||||
color="primary"
|
||||
disabled={loading}
|
||||
>
|
||||
{loading ? (
|
||||
<CircularProgress size={24} />
|
||||
) : (
|
||||
"Submit"
|
||||
)}
|
||||
{loading ? <CircularProgress size={24} /> : "Submit"}
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
@ -303,7 +303,7 @@ const TerritoryManager = () => {
|
||||
>
|
||||
<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
|
||||
</button>
|
||||
@ -313,7 +313,7 @@ const TerritoryManager = () => {
|
||||
>
|
||||
<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
|
||||
</button>
|
||||
|
Loading…
Reference in New Issue
Block a user