This commit is contained in:
Sibunnayak 2024-10-18 16:09:35 +05:30
parent abc4feee63
commit 7bba1cdc22
3 changed files with 232 additions and 214 deletions

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useRef, useCallback } from "react";
import axios from "axios"; import axios from "axios";
import { isAutheticated } from "src/auth"; import { isAutheticated } from "src/auth";
import { import {
@ -13,7 +13,8 @@ import {
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import { ClipLoader } from "react-spinners"; import { ClipLoader } from "react-spinners";
import swal from "sweetalert"; import swal from "sweetalert";
import { toast } from "react-hot-toast";
import debounce from "lodash.debounce";
const style = { const style = {
position: "absolute", position: "absolute",
top: "50%", top: "50%",
@ -28,54 +29,65 @@ const style = {
const Brands = () => { const Brands = () => {
const token = isAutheticated(); const token = isAutheticated();
const nameRef = useRef();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [updating, setUpdating] = useState(true); const [updating, setUpdating] = useState(true);
const [saveLoding, setSaveLoading] = useState(true); const [saveLoading, setSaveLoading] = useState(true);
const [edit, setEdit] = useState(false); const [edit, setEdit] = useState(false);
const [brandName, setbrandName] = useState(""); const [brandName, setBrandName] = useState("");
const [brandId, setbrandId] = useState(""); const [brandId, setBrandId] = useState("");
const [brand, setbrand] = useState([]); const [brands, setBrands] = useState([]);
const [itemPerPage, setItemPerPage] = useState(10); const [itemPerPage, setItemPerPage] = useState(10);
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [olderbrandName, setOlderBrandName] = useState(""); const [olderBrandName, setOlderBrandName] = useState("");
const handleOpen = () => setOpen(true); const handleOpen = () => setOpen(true);
const handleClose = () => { const handleClose = () => {
setOpen(false); setOpen(false);
setEdit(false); setEdit(false);
setbrandName(""); setBrandName("");
setbrandId(""); setBrandId("");
}; };
const getBrands = async () => { const getBrands = async () => {
try { try {
const response = await axios.get("/api/brand/getBrands"); setLoading(true); // Set loading to true before fetching
const response = await axios.get("/api/brand/getBrands", {
params: {
page,
show: itemPerPage,
brandName: nameRef.current?.value || "",
}, // Include pagination and search
});
if (response.status === 200) { if (response.status === 200) {
setbrand(response.data.brands); setBrands(response.data.brands);
setLoading(false);
} }
} catch (error) { } catch (error) {
console.error("Failed to fetch brands:", error); console.error("Failed to fetch brands:", error);
} finally {
setLoading(false); // Set loading to false after fetching
} }
}; };
useEffect(() => { useEffect(() => {
getBrands(); getBrands();
}, []); }, [page, itemPerPage]); // Trigger fetch when these values change
const handleEditClick = (_id, brandName) => { const handleEditClick = (_id, brandName) => {
setOpen(true); setOpen(true);
setbrandName(brandName); setBrandName(brandName);
setbrandId(_id); setBrandId(_id);
setOlderBrandName(brandName); setOlderBrandName(brandName);
setEdit(true); setEdit(true);
}; };
const handleUpdate = async () => { const handleUpdate = async () => {
const filteredBrandNames = brand const filteredBrandNames = brands
.filter((brand) => brand.brandName.toLowerCase() !== olderbrandName.toLowerCase()) .filter(
(brand) =>
brand.brandName.toLowerCase() !== olderBrandName.toLowerCase()
)
.map((brand) => brand.brandName.toLowerCase()); .map((brand) => brand.brandName.toLowerCase());
if (filteredBrandNames.includes(brandName.toLowerCase())) { if (filteredBrandNames.includes(brandName.toLowerCase())) {
@ -97,7 +109,7 @@ const Brands = () => {
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
}); });
handleClose(); handleClose();
swal("Success", "The brand was updated successfully!", "success"); toast.success("Brand updated successfully");
getBrands(); getBrands();
} catch (err) { } catch (err) {
swal("Error", "Failed to update brand", "error"); swal("Error", "Failed to update brand", "error");
@ -120,7 +132,7 @@ const Brands = () => {
await axios.delete(`/api/brand/delete/${_id}`, { await axios.delete(`/api/brand/delete/${_id}`, {
headers: { Authorization: `Bearer ${token}` }, headers: { Authorization: `Bearer ${token}` },
}); });
swal("Success", "The brand was deleted successfully!", "success"); toast.success("Brand deleted successfully");
getBrands(); getBrands();
} catch (err) { } catch (err) {
swal("Error", "Failed to delete brand", "error"); swal("Error", "Failed to delete brand", "error");
@ -129,8 +141,12 @@ const Brands = () => {
}); });
}; };
const handleSavebrand = async () => { const handleSaveBrand = async () => {
if (brand.some((brand) => brand.brandName.toLowerCase() === brandName.toLowerCase())) { if (
brands.some(
(brand) => brand.brandName.toLowerCase() === brandName.toLowerCase()
)
) {
swal("Warning", "Brand already exists.", "error"); swal("Warning", "Brand already exists.", "error");
return; return;
} }
@ -148,7 +164,7 @@ const Brands = () => {
await axios.post("/api/brand/add", formData, { await axios.post("/api/brand/add", formData, {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
"Content-Type": "multipart/formdata", "Content-Type": "multipart/form-data",
}, },
}); });
handleClose(); handleClose();
@ -161,8 +177,19 @@ const Brands = () => {
} }
}; };
const getPageCount = () => Math.max(1, Math.ceil(brand.length / itemPerPage)); const getPageCount = () =>
Math.max(1, Math.ceil(brands.length / itemPerPage));
const debouncedSearch = useCallback(
debounce(() => {
setPage(1);
getBrands();
}, 500),
[]
);
const handleSearchChange = () => {
debouncedSearch();
};
return ( return (
<div className="main-content"> <div className="main-content">
<div className="page-content"> <div className="page-content">
@ -191,9 +218,6 @@ const Brands = () => {
textTransform: "capitalize", textTransform: "capitalize",
}} }}
onClick={handleOpen} onClick={handleOpen}
// onClick={() => {
// navigate("/testimonial/new", { replace: true });
// }}
> >
Add New brand Add New brand
</Button> </Button>
@ -229,9 +253,9 @@ const Brands = () => {
padding: "1rem", padding: "1rem",
}} }}
onChange={(e) => onChange={(e) =>
setbrandName( setBrandName(
e.target.value.charAt(0).toUpperCase() + e.target.value.charAt(0).toUpperCase() +
e.target.value.slice(1) e.target.value.slice(1)
) )
} }
/> />
@ -249,7 +273,7 @@ const Brands = () => {
p={2} p={2}
display={"flex"} display={"flex"}
justifyContent={"right"} justifyContent={"right"}
// width={"500px"} // width={"500px"}
> >
{!edit && ( {!edit && (
<button <button
@ -257,7 +281,7 @@ const Brands = () => {
color: "white", color: "white",
marginRight: "1rem", marginRight: "1rem",
}} }}
onClick={() => handleSavebrand()} onClick={() => handleSaveBrand()}
type="button" type="button"
className=" className="
btn btn-primary btn-sm btn btn-primary btn-sm
@ -267,8 +291,8 @@ const Brands = () => {
mt-1 mt-1
" "
> >
<ClipLoader loading={!saveLoding} size={18} /> <ClipLoader loading={!saveLoading} size={18} />
{saveLoding && "Save"} {saveLoading && "Save"}
</button> </button>
)} )}
{edit && ( {edit && (
@ -322,18 +346,17 @@ const Brands = () => {
<div className="card"> <div className="card">
<div className="card-body"> <div className="card-body">
<div className="row ml-0 mr-0 mb-10"> <div className="row ml-0 mr-0 mb-10">
<div className="col-sm-12 col-md-12"> <div className="col-lg-1">
<div className="dataTables_length"> <div className="dataTables_length">
<label className="w-100"> <label className="w-100">
Show Show
<select <select
style={{ width: "10%" }} onChange={(e) => {
onChange={(e) => setItemPerPage(e.target.value)} setItemPerPage(e.target.value);
className=" setPage(1);
select-w }}
custom-select custom-select-sm className="form-control"
form-control form-control-sm disabled={loading}
"
> >
<option value="10">10</option> <option value="10">10</option>
<option value="25">25</option> <option value="25">25</option>
@ -344,6 +367,17 @@ const Brands = () => {
</label> </label>
</div> </div>
</div> </div>
<div className="col-lg-3">
<label>Brand Name:</label>
<input
type="text"
placeholder="product name"
className="form-control"
ref={nameRef}
onChange={handleSearchChange}
disabled={loading}
/>
</div>
</div> </div>
<div className="table-responsive table-shoot mt-3"> <div className="table-responsive table-shoot mt-3">
@ -356,16 +390,15 @@ const Brands = () => {
style={{ background: "rgb(140, 213, 213)" }} style={{ background: "rgb(140, 213, 213)" }}
> >
<tr> <tr>
<th> Brand Name</th> <th> Brand Name</th>
<th>Action</th> <th>Action</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{!loading && brand.length === 0 && ( {!loading && brands.length === 0 && (
<tr className="text-center"> <tr className="text-center">
<td colSpan="6"> <td colSpan="2">
<h5>No Data Available</h5> <h5>No Data Available</h5>
</td> </td>
</tr> </tr>
@ -377,61 +410,53 @@ const Brands = () => {
</td> </td>
</tr> </tr>
) : ( ) : (
brand && brands &&
brand brands.map((item, i) => (
.slice( <tr key={i}>
(`${page}` - 1) * itemPerPage, <td>
`${page}` * itemPerPage <h5>{item.brandName} </h5>
) </td>
.map((item, i) => ( <td className="text-start">
<tr key={i}> <button
<td> style={{
<h5>{item.brandName} </h5> color: "white",
</td> marginRight: "1rem",
<td className="text-start"> }}
<button type="button"
style={{ className="
color: "white",
marginRight: "1rem",
}}
type="button"
className="
btn btn-primary btn-sm btn btn-primary btn-sm
waves-effect waves-light waves-effect waves-light
btn-table btn-table
mx-1 mx-1
mt-1 mt-1
" "
onClick={() => onClick={() =>
handleEditClick( handleEditClick(item._id, item.brandName)
item._id, }
item.brandName, >
) Edit
} </button>
> <button
Edit style={{
</button> color: "white",
<button marginRight: "1rem",
style={{ background: "red",
color: "white", }}
marginRight: "1rem", type="button"
background: "red", className="
}}
type="button"
className="
btn btn-sm btn btn-sm
waves-effect waves-light waves-effect waves-light
btn-table btn-table
mx-1 mx-1
mt-1 mt-1
" "
onClick={() => handleDelete(item._id)} onClick={() => handleDelete(item._id)}
> >
Delete Delete
</button> </button>
</td> </td>
</tr> </tr>
)) ))
)} )}
</tbody> </tbody>
</table> </table>

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useRef, useCallback } from "react";
import axios from "axios"; import axios from "axios";
import { isAutheticated } from "src/auth"; import { isAutheticated } from "src/auth";
import { import {
@ -13,7 +13,8 @@ import {
import CloseIcon from "@mui/icons-material/Close"; import CloseIcon from "@mui/icons-material/Close";
import { ClipLoader } from "react-spinners"; import { ClipLoader } from "react-spinners";
import swal from "sweetalert"; import swal from "sweetalert";
import { toast } from "react-hot-toast";
import debounce from "lodash.debounce";
const style = { const style = {
position: "absolute", position: "absolute",
top: "50%", top: "50%",
@ -28,6 +29,7 @@ const style = {
const Categories = () => { const Categories = () => {
const token = isAutheticated(); const token = isAutheticated();
const nameRef = useRef();
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [updating, setUpdating] = useState(true); const [updating, setUpdating] = useState(true);
const [saveLoding, setSaveLoading] = useState(true); const [saveLoding, setSaveLoading] = useState(true);
@ -40,7 +42,6 @@ const Categories = () => {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [olderCategoryName, setOlderCategoruName] = useState(""); const [olderCategoryName, setOlderCategoruName] = useState("");
const handleOpen = () => setOpen(true); const handleOpen = () => setOpen(true);
const handleClose = () => { const handleClose = () => {
setOpen(false); setOpen(false);
@ -53,24 +54,28 @@ const Categories = () => {
const getCategories = async () => { const getCategories = async () => {
try { try {
setLoading(true);
const response = await axios.get("/api/category/getCategories", { const response = await axios.get("/api/category/getCategories", {
// headers: { params: {
// Authorization: `Bearer ${token}`, page,
// }, show: itemPerPage,
categoryName: nameRef.current?.value || "",
}, // Include pagination and search
}); });
if (response.status === 200) { if (response.status === 200) {
setCategory(response?.data?.categories); setCategory(response?.data?.categories);
setLoading(false);
} }
} catch (error) { } catch (error) {
console.error("Failed to fetch brands:", error); console.error("Failed to fetch brands:", error);
} finally {
setLoading(false); // Set loading to false after fetching
} }
}; };
useEffect(() => { useEffect(() => {
getCategories(); getCategories();
}, []); }, [page, itemPerPage]);
const handleEditClick = (_id, categoryName) => { const handleEditClick = (_id, categoryName) => {
setOpen(true); setOpen(true);
@ -82,12 +87,14 @@ const Categories = () => {
}; };
const handleUpdate = async () => { const handleUpdate = async () => {
const filteredArrayNames = category.filter( const filteredArrayNames = category
(item) => item.categoryName.toLowerCase() !== olderCategoryName.toLowerCase()) .filter(
.map((item) => item.categoryName.toLowerCase() (item) =>
); item.categoryName.toLowerCase() !== olderCategoryName.toLowerCase()
)
.map((item) => item.categoryName.toLowerCase());
// console.log(filteredArrayNames, "filter"); // console.log(filteredArrayNames, "filter");
if(filteredArrayNames.includes(categoryName.toLowerCase())){ if (filteredArrayNames.includes(categoryName.toLowerCase())) {
swal({ swal({
title: "Warning", title: "Warning",
text: "Category already exists ", text: "Category already exists ",
@ -109,22 +116,16 @@ const Categories = () => {
setUpdating(false); setUpdating(false);
const formData = new FormData(); const formData = new FormData();
formData.append("categoryName", categoryName); formData.append("categoryName", categoryName);
try{ try {
await axios await axios.patch(`/api/category/update/${categoryId}`, formData, {
.patch(`/api/category/update/${categoryId}`, formData, {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}) });
handleClose(); handleClose();
swal({ toast.success("Category updated successfully");
title: "Congratulations!!", getCategories();
text: "The category was updated successfully!", } catch (err) {
icon: "success",
button: "OK",
});
getCategories();
}catch(err){
swal({ swal({
title: "", title: "",
text: "Something went wrong!", text: "Something went wrong!",
@ -132,7 +133,7 @@ try{
button: "Retry", button: "Retry",
dangerMode: true, dangerMode: true,
}); });
}finally{ } finally {
setUpdating(true); setUpdating(true);
} }
}; };
@ -148,33 +149,32 @@ try{
}).then(async (value) => { }).then(async (value) => {
if (value === true) { if (value === true) {
try { try {
await axios await axios.delete(`/api/category/delete/${_id}`, {
.delete(`/api/category/delete/${_id}`, {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
}, },
}); });
swal({ toast.success("Category deleted successfully");
title: "Congratulations!!", getCategories();
text: "The category was deleted successfully!", } catch (err) {
icon: "success", swal({
button: "OK", title: "",
}); text: "Something went wrong!",
getCategories(); icon: "error",
}catch(err) { button: "Retry",
swal({ dangerMode: true,
title: "", });
text: "Something went wrong!", }
icon: "error",
button: "Retry",
dangerMode: true,
});
} }
}}); });
}; };
const handleSaveCategory = async () => { const handleSaveCategory = async () => {
if(category.some((item) => item.categoryName.toLowerCase() === categoryName.toLowerCase())){ if (
category.some(
(item) => item.categoryName.toLowerCase() === categoryName.toLowerCase()
)
) {
swal({ swal({
title: "Warning", title: "Warning",
text: "Category already exists ", text: "Category already exists ",
@ -198,23 +198,17 @@ try{
setLoading(true); setLoading(true);
const formData = new FormData(); const formData = new FormData();
formData.append("categoryName", categoryName); formData.append("categoryName", categoryName);
try{ try {
await axios await axios.post("/api/category/add", formData, {
.post("/api/category/add", formData, {
headers: { headers: {
Authorization: `Bearer ${token}`, Authorization: `Bearer ${token}`,
"Content-Type": "multipart/formdata", "Content-Type": "multipart/formdata",
}, },
})
handleClose();
swal({
title: "Congratulations!!",
text: "The category was added successfully!",
icon: "success",
button: "OK",
}); });
handleClose();
toast.success("Category added successfully");
getCategories(); getCategories();
}catch(err){ } catch (err) {
swal({ swal({
title: "", title: "",
text: "Something went wrong!", text: "Something went wrong!",
@ -222,14 +216,24 @@ try{
button: "Retry", button: "Retry",
dangerMode: true, dangerMode: true,
}); });
}finally{ } finally {
setSaveLoading(true); setSaveLoading(true);
} }
}; };
const getPageCount = () => { const getPageCount = () => {
return Math.max(1, Math.ceil(category.length / itemPerPage)); return Math.max(1, Math.ceil(category.length / itemPerPage));
}; };
const debouncedSearch = useCallback(
debounce(() => {
setPage(1);
getCategories();
}, 500),
[]
);
const handleSearchChange = () => {
debouncedSearch();
};
return ( return (
<div className="main-content"> <div className="main-content">
<div className="page-content"> <div className="page-content">
@ -258,9 +262,6 @@ try{
textTransform: "capitalize", textTransform: "capitalize",
}} }}
onClick={handleOpen} onClick={handleOpen}
// onClick={() => {
// navigate("/testimonial/new", { replace: true });
// }}
> >
Add New Category Add New Category
</Button> </Button>
@ -298,7 +299,7 @@ try{
onChange={(e) => onChange={(e) =>
setCategoryName( setCategoryName(
e.target.value.charAt(0).toUpperCase() + e.target.value.charAt(0).toUpperCase() +
e.target.value.slice(1) e.target.value.slice(1)
) )
} }
/> />
@ -316,7 +317,7 @@ try{
p={2} p={2}
display={"flex"} display={"flex"}
justifyContent={"right"} justifyContent={"right"}
// width={"500px"} // width={"500px"}
> >
{!edit && ( {!edit && (
<button <button
@ -389,18 +390,17 @@ try{
<div className="card"> <div className="card">
<div className="card-body"> <div className="card-body">
<div className="row ml-0 mr-0 mb-10"> <div className="row ml-0 mr-0 mb-10">
<div className="col-sm-12 col-md-12"> <div className="col-lg-1">
<div className="dataTables_length"> <div className="dataTables_length">
<label className="w-100"> <label className="w-100">
Show Show
<select <select
style={{ width: "10%" }} onChange={(e) => {
onChange={(e) => setItemPerPage(e.target.value)} setItemPerPage(e.target.value);
className=" setPage(1);
select-w }}
custom-select custom-select-sm className="form-control"
form-control form-control-sm disabled={loading}
"
> >
<option value="10">10</option> <option value="10">10</option>
<option value="25">25</option> <option value="25">25</option>
@ -411,6 +411,17 @@ try{
</label> </label>
</div> </div>
</div> </div>
<div className="col-lg-3">
<label>Category Name:</label>
<input
type="text"
placeholder="product name"
className="form-control"
ref={nameRef}
onChange={handleSearchChange}
disabled={loading}
/>
</div>
</div> </div>
<div className="table-responsive table-shoot mt-3"> <div className="table-responsive table-shoot mt-3">
@ -423,7 +434,6 @@ try{
style={{ background: "rgb(140, 213, 213)" }} style={{ background: "rgb(140, 213, 213)" }}
> >
<tr> <tr>
<th> Category Name</th> <th> Category Name</th>
<th>Action</th> <th>Action</th>
@ -432,7 +442,7 @@ try{
<tbody> <tbody>
{!loading && category.length === 0 && ( {!loading && category.length === 0 && (
<tr className="text-center"> <tr className="text-center">
<td colSpan="6"> <td colSpan="">
<h5>No Data Available</h5> <h5>No Data Available</h5>
</td> </td>
</tr> </tr>
@ -445,60 +455,52 @@ try{
</tr> </tr>
) : ( ) : (
category && category &&
category category.map((item, i) => (
.slice( <tr key={i}>
(`${page}` - 1) * itemPerPage, <td>
`${page}` * itemPerPage <h5>{item.categoryName} </h5>
) </td>
.map((item, i) => ( <td className="text-start">
<tr key={i}> <button
<td> style={{
<h5>{item.categoryName} </h5> color: "white",
</td> marginRight: "1rem",
<td className="text-start"> }}
<button type="button"
style={{ className="
color: "white",
marginRight: "1rem",
}}
type="button"
className="
btn btn-primary btn-sm btn btn-primary btn-sm
waves-effect waves-light waves-effect waves-light
btn-table btn-table
mx-1 mx-1
mt-1 mt-1
" "
onClick={() => onClick={() =>
handleEditClick( handleEditClick(item._id, item.categoryName)
item._id, }
item.categoryName, >
) Edit
} </button>
> <button
Edit style={{
</button> color: "white",
<button marginRight: "1rem",
style={{ background: "red",
color: "white", }}
marginRight: "1rem", type="button"
background: "red", className="
}}
type="button"
className="
btn btn-sm btn btn-sm
waves-effect waves-light waves-effect waves-light
btn-table btn-table
mx-1 mx-1
mt-1 mt-1
" "
onClick={() => handleDelete(item._id)} onClick={() => handleDelete(item._id)}
> >
Delete Delete
</button> </button>
</td> </td>
</tr> </tr>
)) ))
)} )}
</tbody> </tbody>
</table> </table>

View File

@ -6,6 +6,7 @@ import { useNavigate } from "react-router-dom";
import { isAutheticated } from "src/auth"; import { isAutheticated } from "src/auth";
import swal from "sweetalert"; import swal from "sweetalert";
import debounce from "lodash.debounce"; import debounce from "lodash.debounce";
import { toast } from "react-hot-toast";
const Products = () => { const Products = () => {
const token = isAutheticated(); const token = isAutheticated();
const navigate = useNavigate(); const navigate = useNavigate();
@ -138,12 +139,7 @@ const Products = () => {
}, },
}) })
.then((res) => { .then((res) => {
swal({ toast.success("Product deleted successfully!");
title: "Deleted",
text: "Product Deleted successfully!",
icon: "success",
button: "ok",
});
setSuccess((prev) => !prev); setSuccess((prev) => !prev);
}) })
.catch((err) => { .catch((err) => {
@ -179,12 +175,7 @@ const Products = () => {
}, },
}) })
.then((res) => { .then((res) => {
swal({ toast.success("Product status updated successfully!");
title: "Changed",
text: "Product status changed successfully!",
icon: "success",
button: "ok",
});
setSuccess((prev) => !prev); setSuccess((prev) => !prev);
}) })
.catch((err) => { .catch((err) => {