dispatch({ type: "editCategoryModalClose" })}
51 | className={`${
52 | data.editCategoryModal.modal ? "" : "hidden"
53 | } fixed top-0 left-0 z-30 w-full h-full bg-black opacity-50`}
54 | />
55 | {/* End Black Overlay */}
56 |
57 | {/* Modal Start */}
58 |
63 |
64 |
65 |
66 | Add Category
67 |
68 | {/* Close Modal */}
69 |
dispatch({ type: "editCategoryModalClose" })}
72 | className="cursor-pointer text-gray-100 py-2 px-2 rounded-full"
73 | >
74 |
81 |
87 |
88 |
89 |
90 |
91 | Category Description
92 |
102 |
103 | Category Status
104 | setStatus(e.target.value)}
108 | className="px-4 py-2 border focus:outline-none"
109 | id="status"
110 | >
111 |
112 | Active
113 |
114 |
115 | Disabled
116 |
117 |
118 |
119 |
120 | submitForm()}
123 | className="rounded-full bg-gray-800 text-gray-100 text-lg font-medium py-2"
124 | >
125 | Create category
126 |
127 |
128 |
129 |
130 |
131 | );
132 | };
133 |
134 | export default EditCategoryModal;
135 |
--------------------------------------------------------------------------------
/client/src/components/admin/categories/FetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | const BearerToken = () =>
5 | localStorage.getItem("jwt")
6 | ? JSON.parse(localStorage.getItem("jwt")).token
7 | : false;
8 | const Headers = () => {
9 | return {
10 | headers: {
11 | token: `Bearer ${BearerToken()}`,
12 | },
13 | };
14 | };
15 |
16 | export const getAllCategory = async () => {
17 | try {
18 | let res = await axios.get(`${apiURL}/api/category/all-category`, Headers());
19 | return res.data;
20 | } catch (error) {
21 | console.log(error);
22 | }
23 | };
24 |
25 | export const createCategory = async ({
26 | cName,
27 | cImage,
28 | cDescription,
29 | cStatus,
30 | }) => {
31 | let formData = new FormData();
32 | formData.append("cImage", cImage);
33 | formData.append("cName", cName);
34 | formData.append("cDescription", cDescription);
35 | formData.append("cStatus", cStatus);
36 |
37 | try {
38 | let res = await axios.post(
39 | `${apiURL}/api/category/add-category`,
40 | formData,
41 | Headers()
42 | );
43 | return res.data;
44 | } catch (error) {
45 | console.log(error);
46 | }
47 | };
48 |
49 | export const editCategory = async (cId, des, status) => {
50 | let data = { cId: cId, cDescription: des, cStatus: status };
51 | try {
52 | let res = await axios.post(
53 | `${apiURL}/api/category/edit-category`,
54 | data,
55 | Headers()
56 | );
57 | return res.data;
58 | } catch (error) {
59 | console.log(error);
60 | }
61 | };
62 |
63 | export const deleteCategory = async (cId) => {
64 | try {
65 | let res = await axios.post(
66 | `${apiURL}/api/category/delete-category`,
67 | { cId },
68 | Headers()
69 | );
70 | return res.data;
71 | } catch (error) {
72 | console.log(error);
73 | }
74 | };
75 |
--------------------------------------------------------------------------------
/client/src/components/admin/categories/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, createContext, useReducer } from "react";
2 | import AdminLayout from "../layout";
3 | import CategoryMenu from "./CategoryMenu";
4 | import AllCategories from "./AllCategories";
5 | import { categoryState, categoryReducer } from "./CategoryContext";
6 |
7 | /* This context manage all of the caregories component's data */
8 | export const CategoryContext = createContext();
9 |
10 | const CategoryComponent = () => {
11 | return (
12 |
16 | );
17 | };
18 |
19 | const Categories = (props) => {
20 | const [data, dispatch] = useReducer(categoryReducer, categoryState);
21 | return (
22 |
23 |
24 | } />
25 |
26 |
27 | );
28 | };
29 |
30 | export default Categories;
31 |
--------------------------------------------------------------------------------
/client/src/components/admin/dashboardAdmin/Action.js:
--------------------------------------------------------------------------------
1 | import {
2 | DashboardData,
3 | postUploadImage,
4 | getSliderImages,
5 | postDeleteImage,
6 | } from "./FetchApi";
7 | import { getAllOrder } from "../orders/FetchApi.js";
8 |
9 | export const GetAllData = async (dispatch) => {
10 | let responseData = await DashboardData();
11 | if (responseData) {
12 | dispatch({ type: "totalData", payload: responseData });
13 | }
14 | };
15 |
16 | export const todayAllOrders = async (dispatch) => {
17 | let responseData = await getAllOrder();
18 | if (responseData) {
19 | dispatch({ type: "totalOrders", payload: responseData });
20 | }
21 | };
22 |
23 | export const sliderImages = async (dispatch) => {
24 | try {
25 | let responseData = await getSliderImages();
26 | if (responseData && responseData.Images) {
27 | dispatch({ type: "sliderImages", payload: responseData.Images });
28 | }
29 | } catch (error) {
30 | console.log(error);
31 | }
32 | };
33 |
34 | export const deleteImage = async (id, dispatch) => {
35 | dispatch({ type: "imageUpload", payload: true });
36 | try {
37 | let responseData = await postDeleteImage(id);
38 | if (responseData && responseData.success) {
39 | setTimeout(function () {
40 | sliderImages(dispatch);
41 | dispatch({ type: "imageUpload", payload: false });
42 | }, 1000);
43 | }
44 | } catch (error) {
45 | console.log(error);
46 | }
47 | };
48 |
49 | export const uploadImage = async (image, dispatch) => {
50 | dispatch({ type: "imageUpload", payload: true });
51 | let formData = new FormData();
52 | formData.append("image", image);
53 | console.log(formData.get("image"));
54 | try {
55 | let responseData = await postUploadImage(formData);
56 | if (responseData && responseData.success) {
57 | setTimeout(function () {
58 | dispatch({ type: "imageUpload", payload: false });
59 | sliderImages(dispatch);
60 | }, 1000);
61 | }
62 | } catch (error) {
63 | console.log(error);
64 | }
65 | };
66 |
--------------------------------------------------------------------------------
/client/src/components/admin/dashboardAdmin/DashboardContext.js:
--------------------------------------------------------------------------------
1 | export const dashboardState = {
2 | totalData: [],
3 | totalOrders: [],
4 | uploadSliderBtn: true,
5 | imageUpload: false,
6 | sliderImages: [],
7 | };
8 |
9 | export const dashboardReducer = (state, action) => {
10 | switch (action.type) {
11 | case "totalData":
12 | return {
13 | ...state,
14 | totalData: action.payload,
15 | };
16 | case "totalOrders":
17 | return {
18 | ...state,
19 | totalOrders: action.payload,
20 | };
21 | case "uploadSliderBtn":
22 | return {
23 | ...state,
24 | uploadSliderBtn: action.payload,
25 | };
26 | case "imageUpload":
27 | return {
28 | ...state,
29 | imageUpload: action.payload,
30 | };
31 | case "sliderImages":
32 | return {
33 | ...state,
34 | sliderImages: action.payload,
35 | };
36 | default:
37 | return state;
38 | }
39 | };
40 |
--------------------------------------------------------------------------------
/client/src/components/admin/dashboardAdmin/FetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | export const DashboardData = async () => {
5 | try {
6 | let res = await axios.post(`${apiURL}/api/customize/dashboard-data`);
7 | return res.data;
8 | } catch (error) {
9 | console.log(error);
10 | }
11 | };
12 |
13 | export const getSliderImages = async () => {
14 | try {
15 | let res = await axios.get(`${apiURL}/api/customize/get-slide-image`);
16 | return res.data;
17 | } catch (error) {
18 | console.log(error);
19 | }
20 | };
21 |
22 | export const postUploadImage = async (formData) => {
23 | try {
24 | let res = await axios.post(
25 | `${apiURL}/api/customize/upload-slide-image`,
26 | formData
27 | );
28 | return res.data;
29 | } catch (error) {
30 | console.log(error);
31 | }
32 | };
33 |
34 | export const postDeleteImage = async (id) => {
35 | try {
36 | let res = await axios.post(`${apiURL}/api/customize/delete-slide-image`, {
37 | id,
38 | });
39 | return res.data;
40 | } catch (error) {
41 | console.log(error);
42 | }
43 | };
44 |
--------------------------------------------------------------------------------
/client/src/components/admin/dashboardAdmin/TodaySell.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext, useEffect } from "react";
2 | import { useHistory } from "react-router-dom";
3 | import moment from "moment";
4 | import { DashboardContext } from "./";
5 | import { todayAllOrders } from "./Action";
6 |
7 | const apiURL = process.env.REACT_APP_API_URL;
8 |
9 | const SellTable = () => {
10 | const history = useHistory();
11 | const { data, dispatch } = useContext(DashboardContext);
12 |
13 | useEffect(() => {
14 | todayAllOrders(dispatch);
15 | // eslint-disable-next-line react-hooks/exhaustive-deps
16 | }, []);
17 |
18 | const ordersList = () => {
19 | let newList = [];
20 | if (data.totalOrders.Orders !== undefined) {
21 | data.totalOrders.Orders.forEach(function (elem) {
22 | if (moment(elem.createdAt).format("LL") === moment().format("LL")) {
23 | newList = [...newList, elem];
24 | }
25 | });
26 | }
27 | return newList;
28 | };
29 |
30 | return (
31 |
32 |
33 |
34 | Today's Orders{" "}
35 | {data.totalOrders.Orders !== undefined ? ordersList().length : 0}
36 |
37 |
38 |
39 |
40 | Products
41 | Image
42 | Status
43 | Order Address
44 | Ordered at
45 |
46 |
47 |
48 | {data.totalOrders.Orders !== undefined ? (
49 | ordersList().map((item, key) => {
50 | return ;
51 | })
52 | ) : (
53 |
54 |
58 | No orders found today
59 |
60 |
61 | )}
62 |
63 |
64 |
65 | Total{" "}
66 | {data.totalOrders.Orders !== undefined ? ordersList().length : 0}{" "}
67 | orders found
68 |
69 |
70 | history.push("/admin/dashboard/orders")}
72 | style={{ background: "#303031" }}
73 | className="cursor-pointer px-4 py-2 text-white rounded-full"
74 | >
75 | View All
76 |
77 |
78 |
79 |
80 | );
81 | };
82 |
83 | const TodayOrderTable = ({ order }) => {
84 | return (
85 |
86 |
87 |
88 | {order.allProduct.map((item, index) => {
89 | return (
90 |
91 | {item.id.pName}
92 | {item.quantitiy}x
93 |
94 | );
95 | })}
96 |
97 |
98 | {order.allProduct.map((item, index) => {
99 | return (
100 |
106 | );
107 | })}
108 |
109 |
110 | {order.status === "Not processed" && (
111 |
112 | {order.status}
113 |
114 | )}
115 | {order.status === "Processing" && (
116 |
117 | {order.status}
118 |
119 | )}
120 | {order.status === "Shipped" && (
121 |
122 | {order.status}
123 |
124 | )}
125 | {order.status === "Delivered" && (
126 |
127 | {order.status}
128 |
129 | )}
130 | {order.status === "Cancelled" && (
131 |
132 | {order.status}
133 |
134 | )}
135 |
136 | {order.address}
137 |
138 | {moment(order.createdAt).format("lll")}
139 |
140 |
141 |
142 | );
143 | };
144 |
145 | const TodaySell = (props) => {
146 | return (
147 |
148 |
149 |
150 | );
151 | };
152 |
153 | export default TodaySell;
154 |
--------------------------------------------------------------------------------
/client/src/components/admin/dashboardAdmin/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, createContext, useReducer } from "react";
2 | import AdminLayout from "../layout";
3 | import DashboardCard from "./DashboardCard";
4 | import Customize from "./Customize";
5 | import { dashboardState, dashboardReducer } from "./DashboardContext";
6 | import TodaySell from "./TodaySell";
7 |
8 | export const DashboardContext = createContext();
9 |
10 | const DashboardComponent = () => {
11 | return (
12 |
13 |
14 |
15 |
16 |
17 | );
18 | };
19 |
20 | const DashboardAdmin = (props) => {
21 | const [data, dispatch] = useReducer(dashboardReducer, dashboardState);
22 | return (
23 |
24 |
25 | } />
26 |
27 |
28 | );
29 | };
30 |
31 | export default DashboardAdmin;
32 |
--------------------------------------------------------------------------------
/client/src/components/admin/index.js:
--------------------------------------------------------------------------------
1 | import DashboardAdmin from "./dashboardAdmin";
2 | import Categories from "./categories";
3 | import Products from "./products";
4 | import Orders from "./orders";
5 |
6 | export { DashboardAdmin, Categories, Products, Orders };
7 |
--------------------------------------------------------------------------------
/client/src/components/admin/layout/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 |
3 | import AdminNavber from "../partials/AdminNavber";
4 | import AdminSidebar from "../partials/AdminSidebar";
5 | import AdminFooter from "../partials/AdminFooter";
6 |
7 | const AdminLayout = ({ children }) => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | {/* All Children pass from here */}
15 | {children}
16 |
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | export default AdminLayout;
24 |
--------------------------------------------------------------------------------
/client/src/components/admin/orders/Actions.js:
--------------------------------------------------------------------------------
1 | import { getAllOrder, deleteOrder } from "./FetchApi";
2 |
3 | export const fetchData = async (dispatch) => {
4 | dispatch({ type: "loading", payload: true });
5 | let responseData = await getAllOrder();
6 | setTimeout(() => {
7 | if (responseData && responseData.Orders) {
8 | dispatch({
9 | type: "fetchOrderAndChangeState",
10 | payload: responseData.Orders,
11 | });
12 | dispatch({ type: "loading", payload: false });
13 | }
14 | }, 1000);
15 | };
16 |
17 | /* This method call the editmodal & dispatch category context */
18 | export const editOrderReq = (oId, type, status, dispatch) => {
19 | if (type) {
20 | console.log("click update");
21 | dispatch({ type: "updateOrderModalOpen", oId: oId, status: status });
22 | }
23 | };
24 |
25 | export const deleteOrderReq = async (oId, dispatch) => {
26 | let responseData = await deleteOrder(oId);
27 | console.log(responseData);
28 | if (responseData && responseData.success) {
29 | fetchData(dispatch);
30 | }
31 | };
32 |
33 | /* Filter All Order */
34 | export const filterOrder = async (
35 | type,
36 | data,
37 | dispatch,
38 | dropdown,
39 | setDropdown
40 | ) => {
41 | let responseData = await getAllOrder();
42 | if (responseData && responseData.Orders) {
43 | let newData;
44 | if (type === "All") {
45 | dispatch({
46 | type: "fetchOrderAndChangeState",
47 | payload: responseData.Orders,
48 | });
49 | setDropdown(!dropdown);
50 | } else if (type === "Not processed") {
51 | newData = responseData.Orders.filter(
52 | (item) => item.status === "Not processed"
53 | );
54 | dispatch({ type: "fetchOrderAndChangeState", payload: newData });
55 | setDropdown(!dropdown);
56 | } else if (type === "Processing") {
57 | newData = responseData.Orders.filter(
58 | (item) => item.status === "Processing"
59 | );
60 | dispatch({ type: "fetchOrderAndChangeState", payload: newData });
61 | setDropdown(!dropdown);
62 | } else if (type === "Shipped") {
63 | newData = responseData.Orders.filter((item) => item.status === "Shipped");
64 | dispatch({ type: "fetchOrderAndChangeState", payload: newData });
65 | setDropdown(!dropdown);
66 | } else if (type === "Delivered") {
67 | newData = responseData.Orders.filter(
68 | (item) => item.status === "Delivered"
69 | );
70 | dispatch({ type: "fetchOrderAndChangeState", payload: newData });
71 | setDropdown(!dropdown);
72 | } else if (type === "Cancelled") {
73 | newData = responseData.Orders.filter(
74 | (item) => item.status === "Cancelled"
75 | );
76 | dispatch({ type: "fetchOrderAndChangeState", payload: newData });
77 | setDropdown(!dropdown);
78 | }
79 | }
80 | };
81 |
--------------------------------------------------------------------------------
/client/src/components/admin/orders/FetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | export const getAllOrder = async () => {
5 | try {
6 | let res = await axios.get(`${apiURL}/api/order/get-all-orders`);
7 | return res.data;
8 | } catch (error) {
9 | console.log(error);
10 | }
11 | };
12 |
13 | export const editCategory = async (oId, status) => {
14 | let data = { oId: oId, status: status };
15 | console.log(data);
16 | try {
17 | let res = await axios.post(`${apiURL}/api/order/update-order`, data);
18 | return res.data;
19 | } catch (error) {
20 | console.log(error);
21 | }
22 | };
23 |
24 | export const deleteOrder = async (oId) => {
25 | let data = { oId: oId };
26 | try {
27 | let res = await axios.post(`${apiURL}/api/order/delete-order`, data);
28 | return res.data;
29 | } catch (error) {
30 | console.log(error);
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/client/src/components/admin/orders/OrderContext.js:
--------------------------------------------------------------------------------
1 | export const orderState = {
2 | orders: [],
3 | addCategoryModal: false,
4 | updateOrderModal: {
5 | modal: false,
6 | oId: null,
7 | status: "",
8 | },
9 | loading: false,
10 | };
11 |
12 | export const orderReducer = (state, action) => {
13 | switch (action.type) {
14 | /* Get all category */
15 | case "fetchOrderAndChangeState":
16 | return {
17 | ...state,
18 | orders: action.payload,
19 | };
20 | /* Create a category */
21 | case "addCategoryModal":
22 | return {
23 | ...state,
24 | addCategoryModal: action.payload,
25 | };
26 | /* Edit a category */
27 | case "updateOrderModalOpen":
28 | return {
29 | ...state,
30 | updateOrderModal: {
31 | modal: true,
32 | oId: action.oId,
33 | status: action.status,
34 | },
35 | };
36 | case "updateOrderModalClose":
37 | return {
38 | ...state,
39 | updateOrderModal: {
40 | modal: false,
41 | oId: null,
42 | status: "",
43 | },
44 | };
45 | case "loading":
46 | return {
47 | ...state,
48 | loading: action.payload,
49 | };
50 | default:
51 | return state;
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/client/src/components/admin/orders/OrderMenu.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useContext } from "react";
2 | import { OrderContext } from "./index";
3 | import UpdateOrderModal from "./UpdateOrderModal";
4 | import SearchFilter from "./SearchFilter";
5 | import { filterOrder } from "./Actions";
6 |
7 | const OrderMenu = (props) => {
8 | const { data, dispatch } = useContext(OrderContext);
9 | const [dropdown, setDropdown] = useState(false);
10 | return (
11 |
12 |
13 |
14 | {/* It's open the add order modal */}
15 |
19 |
setDropdown(!dropdown)}
21 | className="flex items-center cursor-pointer rounded-full overflow-hidden p-2 justify-center"
22 | >
23 |
29 |
30 |
31 |
Filter
32 |
33 |
39 |
41 | filterOrder("All", data, dispatch, dropdown, setDropdown)
42 | }
43 | className="px-4 py-2 hover:bg-black text-center cursor-pointer"
44 | >
45 | All
46 |
47 |
49 | filterOrder(
50 | "Not processed",
51 | data,
52 | dispatch,
53 | dropdown,
54 | setDropdown
55 | )
56 | }
57 | className="px-4 py-2 hover:bg-black text-center cursor-pointer"
58 | >
59 | Not processed
60 |
61 |
63 | filterOrder(
64 | "Processing",
65 | data,
66 | dispatch,
67 | dropdown,
68 | setDropdown
69 | )
70 | }
71 | className="px-4 py-2 hover:bg-black text-center cursor-pointer"
72 | >
73 | Processing
74 |
75 |
77 | filterOrder("Shipped", data, dispatch, dropdown, setDropdown)
78 | }
79 | className="px-4 py-2 hover:bg-black text-center cursor-pointer"
80 | >
81 | Shipped
82 |
83 |
85 | filterOrder(
86 | "Delivered",
87 | data,
88 | dispatch,
89 | dropdown,
90 | setDropdown
91 | )
92 | }
93 | className="px-4 py-2 hover:bg-black text-center cursor-pointer"
94 | >
95 | Delivered
96 |
97 |
99 | filterOrder(
100 | "Cancelled",
101 | data,
102 | dispatch,
103 | dropdown,
104 | setDropdown
105 | )
106 | }
107 | className="px-4 py-2 hover:bg-black text-center cursor-pointer"
108 | >
109 | Cancelled
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 | {/*
*/}
118 |
119 |
120 |
121 | );
122 | };
123 |
124 | export default OrderMenu;
125 |
--------------------------------------------------------------------------------
/client/src/components/admin/orders/SearchFilter.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 |
3 | const SearchFilter = (props) => {
4 | return (
5 |
6 |
7 |
8 |
14 |
19 |
20 |
21 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default SearchFilter;
32 |
--------------------------------------------------------------------------------
/client/src/components/admin/orders/UpdateOrderModal.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext, useState, useEffect } from "react";
2 | import { OrderContext } from "./index";
3 | import { getAllOrder, editCategory } from "./FetchApi";
4 |
5 | const UpdateOrderModal = (props) => {
6 | const { data, dispatch } = useContext(OrderContext);
7 |
8 | const [status, setStatus] = useState("");
9 |
10 | const [oId, setOid] = useState("");
11 |
12 | useEffect(() => {
13 | setOid(data.updateOrderModal.oId);
14 | setStatus(data.updateOrderModal.status);
15 | // eslint-disable-next-line react-hooks/exhaustive-deps
16 | }, [data.updateOrderModal.modal]);
17 |
18 | const fetchData = async () => {
19 | let responseData = await getAllOrder();
20 | if (responseData.Orders) {
21 | dispatch({
22 | type: "fetchOrderAndChangeState",
23 | payload: responseData.Orders,
24 | });
25 | }
26 | };
27 |
28 | const submitForm = async () => {
29 | dispatch({ type: "loading", payload: true });
30 | let responseData = await editCategory(oId, status);
31 | if (responseData.error) {
32 | dispatch({ type: "loading", payload: false });
33 | } else if (responseData.success) {
34 | console.log(responseData.success);
35 | dispatch({ type: "updateOrderModalClose" });
36 | fetchData();
37 | dispatch({ type: "loading", payload: false });
38 | }
39 | };
40 |
41 | return (
42 |
43 | {/* Black Overlay */}
44 | dispatch({ type: "updateOrderModalClose" })}
46 | className={`${
47 | data.updateOrderModal.modal ? "" : "hidden"
48 | } fixed top-0 left-0 z-30 w-full h-full bg-black opacity-50`}
49 | />
50 | {/* End Black Overlay */}
51 |
52 | {/* Modal Start */}
53 |
58 |
59 |
60 |
61 | Update Order
62 |
63 | {/* Close Modal */}
64 |
dispatch({ type: "updateOrderModalClose" })}
67 | className="cursor-pointer text-gray-100 py-2 px-2 rounded-full"
68 | >
69 |
76 |
82 |
83 |
84 |
85 |
86 | Order Status
87 | setStatus(e.target.value)}
91 | className="px-4 py-2 border focus:outline-none"
92 | id="status"
93 | >
94 |
95 | Not processed
96 |
97 |
98 | Processing
99 |
100 |
101 | Shipped
102 |
103 |
104 | Delivered
105 |
106 |
107 | Cancelled
108 |
109 |
110 |
111 |
112 | submitForm()}
115 | className="rounded-full bg-gray-800 text-gray-100 text-lg font-medium py-2"
116 | >
117 | Update category
118 |
119 |
120 |
121 |
122 |
123 | );
124 | };
125 |
126 | export default UpdateOrderModal;
127 |
--------------------------------------------------------------------------------
/client/src/components/admin/orders/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, createContext, useReducer } from "react";
2 | import AdminLayout from "../layout";
3 | import OrderMenu from "./OrderMenu";
4 | import AllOrders from "./AllOrders";
5 | import { orderState, orderReducer } from "./OrderContext";
6 |
7 | /* This context manage all of the orders component's data */
8 | export const OrderContext = createContext();
9 |
10 | const OrderComponent = () => {
11 | return (
12 |
16 | );
17 | };
18 |
19 | const Orders = (props) => {
20 | const [data, dispatch] = useReducer(orderReducer, orderState);
21 | return (
22 |
23 |
24 | } />
25 |
26 |
27 | );
28 | };
29 |
30 | export default Orders;
31 |
--------------------------------------------------------------------------------
/client/src/components/admin/partials/AdminFooter.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import moment from "moment";
3 |
4 | const AdminFooter = (props) => {
5 | return (
6 |
7 |
11 | Develop & Design Hasan-py © Copyright {moment().format("YYYY")}
12 |
13 |
14 | );
15 | };
16 |
17 | export default AdminFooter;
18 |
--------------------------------------------------------------------------------
/client/src/components/admin/partials/AdminSidebar.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import { useLocation, useHistory } from "react-router-dom";
3 |
4 | const AdminSidebar = (props) => {
5 | const location = useLocation();
6 | const history = useHistory();
7 |
8 | return (
9 |
10 |
124 |
125 | );
126 | };
127 |
128 | export default AdminSidebar;
129 |
--------------------------------------------------------------------------------
/client/src/components/admin/products/FetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | export const getAllProduct = async () => {
5 | try {
6 | let res = await axios.get(`${apiURL}/api/product/all-product`);
7 | return res.data;
8 | } catch (error) {
9 | console.log(error);
10 | }
11 | };
12 |
13 | export const createPorductImage = async ({ pImage }) => {
14 | /* Most important part for uploading multiple image */
15 | let formData = new FormData();
16 | for (const file of pImage) {
17 | formData.append("pImage", file);
18 | }
19 | /* Most important part for uploading multiple image */
20 | };
21 |
22 | export const createProduct = async ({
23 | pName,
24 | pDescription,
25 | pImage,
26 | pStatus,
27 | pCategory,
28 | pQuantity,
29 | pPrice,
30 | pOffer,
31 | }) => {
32 | /* Most important part for uploading multiple image */
33 | let formData = new FormData();
34 | for (const file of pImage) {
35 | formData.append("pImage", file);
36 | }
37 | /* Most important part for uploading multiple image */
38 | formData.append("pName", pName);
39 | formData.append("pDescription", pDescription);
40 | formData.append("pStatus", pStatus);
41 | formData.append("pCategory", pCategory);
42 | formData.append("pQuantity", pQuantity);
43 | formData.append("pPrice", pPrice);
44 | formData.append("pOffer", pOffer);
45 |
46 | try {
47 | let res = await axios.post(`${apiURL}/api/product/add-product`, formData);
48 | return res.data;
49 | } catch (error) {
50 | console.log(error);
51 | }
52 | };
53 |
54 | export const editProduct = async (product) => {
55 | console.log(product);
56 | /* Most important part for updating multiple image */
57 | let formData = new FormData();
58 | if (product.pEditImages) {
59 | for (const file of product.pEditImages) {
60 | formData.append("pEditImages", file);
61 | }
62 | }
63 | /* Most important part for updating multiple image */
64 | formData.append("pId", product.pId);
65 | formData.append("pName", product.pName);
66 | formData.append("pDescription", product.pDescription);
67 | formData.append("pStatus", product.pStatus);
68 | formData.append("pCategory", product.pCategory._id);
69 | formData.append("pQuantity", product.pQuantity);
70 | formData.append("pPrice", product.pPrice);
71 | formData.append("pOffer", product.pOffer);
72 | formData.append("pImages", product.pImages);
73 |
74 | try {
75 | let res = await axios.post(`${apiURL}/api/product/edit-product`, formData);
76 | return res.data;
77 | } catch (error) {
78 | console.log(error);
79 | }
80 | };
81 |
82 | export const deleteProduct = async (pId) => {
83 | try {
84 | let res = await axios.post(`${apiURL}/api/product/delete-product`, { pId });
85 | return res.data;
86 | } catch (error) {
87 | console.log(error);
88 | }
89 | };
90 |
91 | export const productByCategory = async (catId) => {
92 | try {
93 | let res = await axios.post(`${apiURL}/api/product/product-by-category`, {
94 | catId,
95 | });
96 | return res.data;
97 | } catch (error) {
98 | console.log(error);
99 | }
100 | };
101 |
102 | export const productByPrice = async (price) => {
103 | try {
104 | let res = await axios.post(`${apiURL}/api/product/product-by-price`, {
105 | price,
106 | });
107 | return res.data;
108 | } catch (error) {
109 | console.log(error);
110 | }
111 | };
112 |
--------------------------------------------------------------------------------
/client/src/components/admin/products/ProductContext.js:
--------------------------------------------------------------------------------
1 | export const productState = {
2 | products: null,
3 | addProductModal: false,
4 | editProductModal: {
5 | modal: false,
6 | pId: "",
7 | pName: "",
8 | pDescription: "",
9 | pImages: null,
10 | pStatus: "",
11 | pCategory: "",
12 | pQuantity: "",
13 | pPrice: "",
14 | pOffer: "",
15 | },
16 | };
17 |
18 | export const productReducer = (state, action) => {
19 | switch (action.type) {
20 | /* Get all product */
21 | case "fetchProductsAndChangeState":
22 | return {
23 | ...state,
24 | products: action.payload,
25 | };
26 | /* Create a product */
27 | case "addProductModal":
28 | return {
29 | ...state,
30 | addProductModal: action.payload,
31 | };
32 | /* Edit a product */
33 | case "editProductModalOpen":
34 | return {
35 | ...state,
36 | editProductModal: {
37 | modal: true,
38 | pId: action.product.pId,
39 | pName: action.product.pName,
40 | pDescription: action.product.pDescription,
41 | pImages: action.product.pImages,
42 | pStatus: action.product.pStatus,
43 | pCategory: action.product.pCategory,
44 | pQuantity: action.product.pQuantity,
45 | pPrice: action.product.pPrice,
46 | pOffer: action.product.pOffer,
47 | },
48 | };
49 | case "editProductModalClose":
50 | return {
51 | ...state,
52 | editProductModal: {
53 | modal: false,
54 | pId: "",
55 | pName: "",
56 | pDescription: "",
57 | pImages: null,
58 | pStatus: "",
59 | pCategory: "",
60 | pQuantity: "",
61 | pPrice: "",
62 | pOffer: "",
63 | },
64 | };
65 | default:
66 | return state;
67 | }
68 | };
69 |
--------------------------------------------------------------------------------
/client/src/components/admin/products/ProductMenu.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from "react";
2 | import { ProductContext } from "./index";
3 | import AddProductModal from "./AddProductModal";
4 | import EditProductModal from "./EditProductModal";
5 |
6 | const ProductMenu = (props) => {
7 | const { dispatch } = useContext(ProductContext);
8 | return (
9 |
10 |
11 |
12 | {/* It's open the add product modal */}
13 |
16 | dispatch({ type: "addProductModal", payload: true })
17 | }
18 | className="rounded-full cursor-pointer p-2 bg-gray-800 flex items-center text-gray-100 text-sm font-semibold uppercase"
19 | >
20 |
26 |
31 |
32 | Add Product
33 |
34 |
35 |
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default ProductMenu;
43 |
--------------------------------------------------------------------------------
/client/src/components/admin/products/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, createContext, useReducer } from "react";
2 | import AdminLayout from "../layout";
3 | import ProductMenu from "./ProductMenu";
4 | import ProductTable from "./ProductTable";
5 | import { productState, productReducer } from "./ProductContext";
6 |
7 | /* This context manage all of the products component's data */
8 | export const ProductContext = createContext();
9 |
10 | const ProductComponent = () => {
11 | return (
12 |
16 | );
17 | };
18 |
19 | const Products = (props) => {
20 | /* To use useReducer make sure that reducer is the first arg */
21 | const [data, dispatch] = useReducer(productReducer, productState);
22 |
23 | return (
24 |
25 |
26 | } />
27 |
28 |
29 | );
30 | };
31 |
32 | export default Products;
33 |
--------------------------------------------------------------------------------
/client/src/components/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | Home,
4 | WishList,
5 | ProtectedRoute,
6 | AdminProtectedRoute,
7 | CartProtectedRoute,
8 | PageNotFound,
9 | ProductDetails,
10 | ProductByCategory,
11 | CheckoutPage,
12 | } from "./shop";
13 | import { DashboardAdmin, Categories, Products, Orders } from "./admin";
14 | import { UserProfile, UserOrders, SettingUser } from "./shop/dashboardUser";
15 |
16 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
17 |
18 | /* Routing All page will be here */
19 | const Routes = (props) => {
20 | return (
21 |
22 |
23 | {/* Shop & Public Routes */}
24 |
25 |
26 |
27 |
32 |
37 | {/* Shop & Public Routes End */}
38 |
39 | {/* Admin Routes */}
40 |
45 |
50 |
55 |
60 | {/* Admin Routes End */}
61 |
62 | {/* User Dashboard */}
63 |
68 |
73 |
78 | {/* User Dashboard End */}
79 |
80 | {/* 404 Page */}
81 |
82 |
83 |
84 | );
85 | };
86 |
87 | export default Routes;
88 |
--------------------------------------------------------------------------------
/client/src/components/shop/auth/AdminProtectedRoute.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Route, Redirect } from "react-router-dom";
3 | import { isAuthenticate, isAdmin } from "./fetchApi";
4 |
5 | const AdminProtectedRoute = ({ component: Component, ...rest }) => (
6 |
9 | isAdmin() && isAuthenticate() ? (
10 |
11 | ) : (
12 |
18 | )
19 | }
20 | />
21 | );
22 |
23 | export default AdminProtectedRoute;
24 |
--------------------------------------------------------------------------------
/client/src/components/shop/auth/CartProtectedRoute.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Route, Redirect } from "react-router-dom";
3 | import { isAuthenticate } from "./fetchApi";
4 |
5 | const CartProtectedRoute = ({ component: Component, ...rest }) => (
6 |
9 | JSON.parse(localStorage.getItem("cart")).length !== 0 &&
10 | isAuthenticate() ? (
11 |
12 | ) : (
13 |
19 | )
20 | }
21 | />
22 | );
23 |
24 | export default CartProtectedRoute;
25 |
--------------------------------------------------------------------------------
/client/src/components/shop/auth/Login.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useContext } from "react";
2 | import { loginReq } from "./fetchApi";
3 | import { LayoutContext } from "../index";
4 | import { useSnackbar } from 'notistack';
5 | const Login = (props) => {
6 | const { data: layoutData, dispatch: layoutDispatch } = useContext(
7 | LayoutContext
8 | );
9 |
10 | const [data, setData] = useState({
11 | email: "",
12 | password: "",
13 | error: false,
14 | loading: true,
15 | });
16 |
17 | const alert = (msg) => {msg}
;
18 |
19 | const { enqueueSnackbar } = useSnackbar();
20 |
21 | const formSubmit = async () => {
22 | setData({ ...data, loading: true });
23 | try {
24 | let responseData = await loginReq({
25 | email: data.email,
26 | password: data.password,
27 | });
28 | if (responseData.error) {
29 | setData({
30 | ...data,
31 | loading: false,
32 | error: responseData.error,
33 | password: "",
34 | });
35 | } else if (responseData.token) {
36 | setData({ email: "", password: "", loading: false, error: false });
37 | localStorage.setItem("jwt", JSON.stringify(responseData));
38 | enqueueSnackbar('Login Completed Successfully..!', { variant: 'success' })
39 | window.location.href = "/";
40 |
41 | }
42 | } catch (error) {
43 | console.log(error);
44 | }
45 | };
46 |
47 | return (
48 |
49 | Login
50 | {layoutData.loginSignupError ? (
51 |
52 | You need to login for checkout. Haven't accont? Create new one.
53 |
54 | ) : (
55 | ""
56 | )}
57 |
118 |
119 | );
120 | };
121 |
122 | export default Login;
123 |
--------------------------------------------------------------------------------
/client/src/components/shop/auth/LoginSignup.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useContext } from "react";
2 | import Login from "./Login";
3 | import Signup from "./Signup";
4 | import { LayoutContext } from "../index";
5 |
6 | const LoginSignup = (props) => {
7 | const { data, dispatch } = useContext(LayoutContext);
8 |
9 | const [login, setLogin] = useState(true);
10 | const [loginValue, setLoginValue] = useState("Create an account");
11 |
12 | const loginSignupModalToggle = () =>
13 | data.loginSignupModal
14 | ? dispatch({ type: "loginSignupModalToggle", payload: false })
15 | : dispatch({ type: "loginSignupModalToggle", payload: true });
16 |
17 | const changeLoginSignup = () => {
18 | if (login) {
19 | setLogin(false);
20 | setLoginValue("Login");
21 | } else {
22 | setLogin(true);
23 | setLoginValue("Create an account");
24 | }
25 | };
26 |
27 | return (
28 |
29 | {/* Black Overlay */}
30 | loginSignupModalToggle()}
32 | className={` ${
33 | data.loginSignupModal ? "" : "hidden"
34 | } fixed top-0 z-40 w-full h-screen bg-black opacity-50 cursor-pointer`}
35 | >
36 | {/* Signup Login Component Render */}
37 |
42 |
43 | {login ?
:
}
44 |
45 |
46 | or
47 |
48 |
49 |
changeLoginSignup()}
51 | style={{ color: "#303031", border: "1px solid #303031" }}
52 | className="px-4 py-2 font-medium text-center cursor-pointer"
53 | >
54 | {loginValue}
55 |
56 | {/* Modal Close Button */}
57 |
58 |
{
60 | loginSignupModalToggle();
61 | dispatch({ type: "loginSignupError", payload: false });
62 | }}
63 | className="w-6 h-6 cursor-pointer"
64 | fill="currentColor"
65 | viewBox="0 0 20 20"
66 | xmlns="http://www.w3.org/2000/svg"
67 | >
68 |
73 |
74 |
75 |
76 |
77 |
78 | );
79 | };
80 |
81 | export default LoginSignup;
82 |
--------------------------------------------------------------------------------
/client/src/components/shop/auth/ProtectedRoute.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Route, Redirect } from "react-router-dom";
3 | import { isAuthenticate, isAdmin } from "./fetchApi";
4 |
5 | const ProtectedRoute = ({ component: Component, ...rest }) => (
6 |
9 | isAuthenticate() && !isAdmin() ? (
10 |
11 | ) : (
12 |
18 | )
19 | }
20 | />
21 | );
22 |
23 | export default ProtectedRoute;
24 |
--------------------------------------------------------------------------------
/client/src/components/shop/auth/Signup.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState } from "react";
2 | import { signupReq } from "./fetchApi";
3 | import { useSnackbar } from 'notistack';
4 | const Signup = (props) => {
5 | const [data, setData] = useState({
6 | name: "",
7 | email: "",
8 | password: "",
9 | cPassword: "",
10 | error: false,
11 | loading: false,
12 | success: false,
13 | });
14 |
15 | const alert = (msg, type) => (
16 | {msg}
17 | );
18 | const { enqueueSnackbar } = useSnackbar();
19 | const formSubmit = async () => {
20 | setData({ ...data, loading: true });
21 | if (data.cPassword !== data.password) {
22 | return setData({
23 | ...data,
24 | error: {
25 | cPassword: "Password doesn't match",
26 | password: "Password doesn't match",
27 | },
28 | });
29 | }
30 | try {
31 | let responseData = await signupReq({
32 | name: data.name,
33 | email: data.email,
34 | password: data.password,
35 | cPassword: data.cPassword,
36 | });
37 | if (responseData.error) {
38 | setData({
39 | ...data,
40 | loading: false,
41 | error: responseData.error,
42 | password: "",
43 | cPassword: "",
44 | });
45 | } else if (responseData.success) {
46 | setData({
47 | success: responseData.success,
48 | name: "",
49 | email: "",
50 | password: "",
51 | cPassword: "",
52 | loading: false,
53 | error: false,
54 | })
55 | enqueueSnackbar('Account Created Successfully..!', { variant: 'success' })
56 | }
57 | } catch (error) {
58 | console.log(error);
59 | }
60 | };
61 |
62 | return (
63 |
64 | Register
65 |
179 |
180 | );
181 | };
182 |
183 | export default Signup;
184 |
--------------------------------------------------------------------------------
/client/src/components/shop/auth/fetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | export const isAuthenticate = () =>
5 | localStorage.getItem("jwt") ? JSON.parse(localStorage.getItem("jwt")) : false;
6 |
7 | export const isAdmin = () =>
8 | localStorage.getItem("jwt")
9 | ? JSON.parse(localStorage.getItem("jwt")).user.role === 1
10 | : false;
11 |
12 | export const loginReq = async ({ email, password }) => {
13 | const data = { email, password };
14 | try {
15 | let res = await axios.post(`${apiURL}/api/signin`, data);
16 | return res.data;
17 | } catch (error) {
18 | console.log(error);
19 | }
20 | };
21 |
22 | export const signupReq = async ({ name, email, password, cPassword }) => {
23 | const data = { name, email, password, cPassword };
24 | try {
25 | let res = await axios.post(`${apiURL}/api/signup`, data);
26 | return res.data;
27 | } catch (error) {
28 | console.log(error);
29 | }
30 | };
31 |
--------------------------------------------------------------------------------
/client/src/components/shop/dashboardUser/Action.js:
--------------------------------------------------------------------------------
1 | import {
2 | getUserById,
3 | updatePersonalInformationFetch,
4 | getOrderByUser,
5 | updatePassword,
6 | } from "./FetchApi";
7 |
8 | export const logout = () => {
9 | localStorage.removeItem("jwt");
10 | localStorage.removeItem("cart");
11 | localStorage.removeItem("wishList");
12 | window.location.href = "/";
13 | };
14 |
15 | export const fetchData = async (dispatch) => {
16 | dispatch({ type: "loading", payload: true });
17 | let userId = JSON.parse(localStorage.getItem("jwt"))
18 | ? JSON.parse(localStorage.getItem("jwt")).user._id
19 | : "";
20 | try {
21 | let responseData = await getUserById(userId);
22 | setTimeout(() => {
23 | if (responseData && responseData.User) {
24 | dispatch({ type: "userDetails", payload: responseData.User });
25 | dispatch({ type: "loading", payload: false });
26 | }
27 | }, 500);
28 | } catch (error) {
29 | console.log(error);
30 | }
31 | };
32 |
33 | export const fetchOrderByUser = async (dispatch) => {
34 | dispatch({ type: "loading", payload: true });
35 | let userId = JSON.parse(localStorage.getItem("jwt"))
36 | ? JSON.parse(localStorage.getItem("jwt")).user._id
37 | : "";
38 | try {
39 | let responseData = await getOrderByUser(userId);
40 | setTimeout(() => {
41 | if (responseData && responseData.Order) {
42 | console.log(responseData);
43 | dispatch({ type: "OrderByUser", payload: responseData.Order });
44 | dispatch({ type: "loading", payload: false });
45 | }
46 | }, 500);
47 | } catch (error) {
48 | console.log(error);
49 | }
50 | };
51 |
52 | export const updatePersonalInformationAction = async (dispatch, fData) => {
53 | const formData = {
54 | uId: fData.id,
55 | name: fData.name,
56 | phoneNumber: fData.phone,
57 | };
58 | dispatch({ type: "loading", payload: true });
59 | try {
60 | let responseData = await updatePersonalInformationFetch(formData);
61 | setTimeout(() => {
62 | if (responseData && responseData.success) {
63 | dispatch({ type: "loading", payload: false });
64 | fetchData(dispatch);
65 | }
66 | }, 500);
67 | } catch (error) {
68 | console.log(error);
69 | }
70 | };
71 |
72 | export const handleChangePassword = async (fData, setFdata, dispatch) => {
73 | if (!fData.newPassword || !fData.oldPassword || !fData.confirmPassword) {
74 | setFdata({
75 | ...fData,
76 | error: "Please provide your all password and a new password",
77 | });
78 | } else if (fData.newPassword !== fData.confirmPassword) {
79 | setFdata({ ...fData, error: "Password does't match" });
80 | } else {
81 | const formData = {
82 | uId: JSON.parse(localStorage.getItem("jwt")).user._id,
83 | oldPassword: fData.oldPassword,
84 | newPassword: fData.newPassword,
85 | };
86 | dispatch({ type: "loading", payload: true });
87 | try {
88 | let responseData = await updatePassword(formData);
89 | if (responseData && responseData.success) {
90 | setFdata({
91 | ...fData,
92 | success: responseData.success,
93 | error: "",
94 | oldPassword: "",
95 | newPassword: "",
96 | confirmPassword: "",
97 | });
98 | dispatch({ type: "loading", payload: false });
99 | } else if (responseData.error) {
100 | dispatch({ type: "loading", payload: false });
101 | setFdata({
102 | ...fData,
103 | error: responseData.error,
104 | success: "",
105 | oldPassword: "",
106 | newPassword: "",
107 | confirmPassword: "",
108 | });
109 | }
110 | } catch (error) {
111 | console.log(error);
112 | }
113 | }
114 | };
115 |
--------------------------------------------------------------------------------
/client/src/components/shop/dashboardUser/DashboardUserContext.js:
--------------------------------------------------------------------------------
1 | export const dashboardUserState = {
2 | userDetails: null,
3 | loading: false,
4 | OrderByUser: null,
5 | };
6 |
7 | export const dashboardUserReducer = (state, action) => {
8 | switch (action.type) {
9 | case "userDetails":
10 | return {
11 | ...state,
12 | userDetails: action.payload,
13 | };
14 | case "OrderByUser":
15 | return {
16 | ...state,
17 | OrderByUser: action.payload,
18 | };
19 | case "loading":
20 | return {
21 | ...state,
22 | loading: action.payload,
23 | };
24 | default:
25 | return state;
26 | }
27 | };
28 |
--------------------------------------------------------------------------------
/client/src/components/shop/dashboardUser/FetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | export const getUserById = async (uId) => {
5 | try {
6 | let res = await axios.post(`${apiURL}/api/user/signle-user`, { uId });
7 | return res.data;
8 | } catch (error) {
9 | console.log(error);
10 | }
11 | };
12 |
13 | export const updatePersonalInformationFetch = async (userData) => {
14 | try {
15 | let res = await axios.post(`${apiURL}/api/user/edit-user`, userData);
16 | return res.data;
17 | } catch (error) {
18 | console.log(error);
19 | }
20 | };
21 |
22 | export const getOrderByUser = async (uId) => {
23 | try {
24 | let res = await axios.post(`${apiURL}/api/order/order-by-user`, { uId });
25 | return res.data;
26 | } catch (error) {
27 | console.log(error);
28 | }
29 | };
30 |
31 | export const updatePassword = async (formData) => {
32 | try {
33 | let res = await axios.post(`${apiURL}/api/user/change-password`, formData);
34 | return res.data;
35 | } catch (error) {
36 | console.log(error);
37 | }
38 | };
39 |
--------------------------------------------------------------------------------
/client/src/components/shop/dashboardUser/Layout.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, createContext, useReducer, useEffect } from "react";
2 | import { Navber, Footer, CartModal } from "../partials";
3 | import Sidebar from "./Sidebar";
4 | import {
5 | dashboardUserState,
6 | dashboardUserReducer,
7 | } from "./DashboardUserContext";
8 | import { fetchData } from "./Action";
9 |
10 | export const DashboardUserContext = createContext();
11 |
12 | const Layout = ({ children }) => {
13 | const [data, dispatch] = useReducer(dashboardUserReducer, dashboardUserState);
14 |
15 | useEffect(() => {
16 | fetchData(dispatch);
17 | }, []);
18 |
19 | return (
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | {/* All Children pass from here */}
28 | {children}
29 |
30 |
31 |
32 |
33 |
34 | );
35 | };
36 |
37 | export default Layout;
38 |
--------------------------------------------------------------------------------
/client/src/components/shop/dashboardUser/SearchFilter.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 |
3 | const SearchFilter = (props) => {
4 | return (
5 |
6 |
35 |
36 | );
37 | };
38 |
39 | export default SearchFilter;
40 |
--------------------------------------------------------------------------------
/client/src/components/shop/dashboardUser/Sidebar.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from "react";
2 | import { useLocation, useHistory } from "react-router-dom";
3 | import { logout } from "./Action";
4 | import { DashboardUserContext } from "./Layout";
5 |
6 | const Sidebar = (props) => {
7 | const { data } = useContext(DashboardUserContext);
8 |
9 | const history = useHistory();
10 | const location = useLocation();
11 |
12 | return (
13 |
14 |
15 |
19 |
26 |
32 |
33 |
34 | Hello,
35 |
36 | {data.userDetails ? data.userDetails.name : ""}
37 |
38 |
39 |
40 |
41 |
history.push("/user/orders")}
43 | className={`${
44 | location.pathname === "/user/orders"
45 | ? "border-r-4 border-yellow-700 bg-gray-200"
46 | : ""
47 | } px-4 py-4 hover:bg-gray-200 cursor-pointer`}
48 | >
49 | My Orders
50 |
51 |
52 |
history.push("/user/profile")}
54 | className={`${
55 | location.pathname === "/user/profile"
56 | ? "border-r-4 border-yellow-700 bg-gray-200"
57 | : ""
58 | } px-4 py-4 hover:bg-gray-200 cursor-pointer`}
59 | >
60 | My Accounts
61 |
62 |
63 |
history.push("/wish-list")}
65 | className={` px-4 py-4 hover:bg-gray-200 cursor-pointer`}
66 | >
67 | My Wishlist
68 |
69 |
70 |
history.push("/user/setting")}
72 | className={`${
73 | location.pathname === "/user/setting"
74 | ? "border-r-4 border-yellow-700 bg-gray-200"
75 | : ""
76 | } px-4 py-4 hover:bg-gray-200 cursor-pointer`}
77 | >
78 | Setting
79 |
80 |
81 |
logout()}
83 | className={`${
84 | location.pathname === "/admin/dashboard/categories"
85 | ? "border-r-4 border-yellow-700 bg-gray-200"
86 | : ""
87 | } px-4 py-4 hover:bg-gray-200 cursor-pointer`}
88 | >
89 | Logout
90 |
91 |
92 |
93 |
94 | );
95 | };
96 |
97 | export default Sidebar;
98 |
--------------------------------------------------------------------------------
/client/src/components/shop/dashboardUser/UserOrders.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect, useContext } from "react";
2 | import moment from "moment";
3 | import { fetchOrderByUser } from "./Action";
4 | import Layout, { DashboardUserContext } from "./Layout";
5 |
6 | const apiURL = process.env.REACT_APP_API_URL;
7 |
8 | const TableHeader = () => {
9 | return (
10 |
11 |
12 |
13 | Products
14 | Status
15 | Total
16 | Phone
17 | Address
18 | Transaction Id
19 | Checkout
20 | Processing
21 |
22 |
23 |
24 | );
25 | };
26 |
27 | const TableBody = ({ order }) => {
28 | return (
29 |
30 |
31 |
32 | {order.allProduct.map((product, i) => {
33 | return (
34 |
35 |
40 | {product.id.pName}
41 | {product.quantitiy}x
42 |
43 | );
44 | })}
45 |
46 |
47 | {order.status === "Not processed" && (
48 |
49 | {order.status}
50 |
51 | )}
52 | {order.status === "Processing" && (
53 |
54 | {order.status}
55 |
56 | )}
57 | {order.status === "Shipped" && (
58 |
59 | {order.status}
60 |
61 | )}
62 | {order.status === "Delivered" && (
63 |
64 | {order.status}
65 |
66 | )}
67 | {order.status === "Cancelled" && (
68 |
69 | {order.status}
70 |
71 | )}
72 |
73 |
74 | ${order.amount}.00
75 |
76 | {order.phone}
77 | {order.address}
78 |
79 | {order.transactionId}
80 |
81 |
82 | {moment(order.createdAt).format("lll")}
83 |
84 |
85 | {moment(order.updatedAt).format("lll")}
86 |
87 |
88 |
89 | );
90 | };
91 |
92 | const OrdersComponent = () => {
93 | const { data, dispatch } = useContext(DashboardUserContext);
94 | const { OrderByUser: orders } = data;
95 |
96 | useEffect(() => {
97 | fetchOrderByUser(dispatch);
98 | // eslint-disable-next-line react-hooks/exhaustive-deps
99 | }, []);
100 |
101 | if (data.loading) {
102 | return (
103 |
119 | );
120 | }
121 | return (
122 |
123 |
124 |
125 |
126 | Orders
127 |
128 |
129 |
130 |
131 |
132 |
133 | {orders && orders.length > 0 ? (
134 | orders.map((item, i) => {
135 | return ;
136 | })
137 | ) : (
138 |
139 |
143 | No order found
144 |
145 |
146 | )}
147 |
148 |
149 |
150 | Total {orders && orders.length} order found
151 |
152 |
153 |
154 |
155 |
156 | );
157 | };
158 |
159 | const UserOrders = (props) => {
160 | return (
161 |
162 | } />
163 |
164 | );
165 | };
166 |
167 | export default UserOrders;
168 |
--------------------------------------------------------------------------------
/client/src/components/shop/dashboardUser/UserProfile.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext, useState, useEffect } from "react";
2 | import Layout from "./Layout";
3 | import { DashboardUserContext } from "./Layout";
4 | import { updatePersonalInformationAction } from "./Action";
5 |
6 | const ProfileComponent = () => {
7 | const { data, dispatch } = useContext(DashboardUserContext);
8 | const userDetails = data.userDetails !== null ? data.userDetails : "";
9 |
10 | const [fData, setFdata] = useState({
11 | id: "",
12 | name: "",
13 | email: "",
14 | phone: "",
15 | success: false,
16 | });
17 |
18 | useEffect(() => {
19 | setFdata({
20 | ...fData,
21 | id: userDetails._id,
22 | name: userDetails.name,
23 | email: userDetails.email,
24 | phone: userDetails.phoneNumber,
25 | });
26 |
27 | // eslint-disable-next-line react-hooks/exhaustive-deps
28 | }, [userDetails]);
29 |
30 | const handleSubmit = () => {
31 | updatePersonalInformationAction(dispatch, fData);
32 | };
33 |
34 | if (data.loading) {
35 | return (
36 |
52 | );
53 | }
54 | return (
55 |
56 |
57 |
58 |
59 | Personal Information
60 |
61 |
62 |
63 | {fData.success ? (
64 |
65 | {fData.success}
66 |
67 | ) : (
68 | ""
69 | )}
70 |
71 | Name
72 | setFdata({ ...fData, name: e.target.value })}
74 | value={fData.name}
75 | type="text"
76 | id="name"
77 | className="border px-4 py-2 w-full focus:outline-none"
78 | />
79 |
80 |
81 | Email
82 |
89 |
90 | You can't change your email
91 |
92 |
93 |
94 | Phone Number
95 | setFdata({ ...fData, phone: e.target.value })}
97 | value={fData.phone}
98 | type="number"
99 | id="number"
100 | className="border px-4 py-2 w-full focus:outline-none"
101 | />
102 |
103 |
handleSubmit()}
105 | style={{ background: "#303031" }}
106 | className="w-full text-center cursor-pointer px-4 py-2 text-gray-100"
107 | >
108 | Update Information
109 |
110 |
111 |
112 |
113 |
114 | );
115 | };
116 |
117 | const UserProfile = (props) => {
118 | return (
119 |
120 | } />
121 |
122 | );
123 | };
124 |
125 | export default UserProfile;
126 |
--------------------------------------------------------------------------------
/client/src/components/shop/dashboardUser/index.js:
--------------------------------------------------------------------------------
1 | import UserProfile from "./UserProfile";
2 | import UserOrders from "./UserOrders";
3 | import SettingUser from "./SettingUser";
4 |
5 | export { UserProfile, UserOrders, SettingUser };
6 |
--------------------------------------------------------------------------------
/client/src/components/shop/home/HomeContext.js:
--------------------------------------------------------------------------------
1 | export const homeState = {
2 | categoryListDropdown: false,
3 | filterListDropdown: false,
4 | searchDropdown: false,
5 | products: null,
6 | loading: false,
7 | sliderImages: [],
8 | };
9 |
10 | export const homeReducer = (state, action) => {
11 | switch (action.type) {
12 | case "categoryListDropdown":
13 | return {
14 | ...state,
15 | categoryListDropdown: action.payload,
16 | filterListDropdown: false,
17 | searchDropdown: false,
18 | };
19 | case "filterListDropdown":
20 | return {
21 | ...state,
22 | categoryListDropdown: false,
23 | filterListDropdown: action.payload,
24 | searchDropdown: false,
25 | };
26 | case "searchDropdown":
27 | return {
28 | ...state,
29 | categoryListDropdown: false,
30 | filterListDropdown: false,
31 | searchDropdown: action.payload,
32 | };
33 | case "setProducts":
34 | return {
35 | ...state,
36 | products: action.payload,
37 | };
38 | case "searchHandleInReducer":
39 | return {
40 | ...state,
41 | products:
42 | action.productArray &&
43 | action.productArray.filter((item) => {
44 | if (
45 | item.pName.toUpperCase().indexOf(action.payload.toUpperCase()) !==
46 | -1
47 | ) {
48 | return item;
49 | }
50 | return null;
51 | }),
52 | };
53 | case "loading":
54 | return {
55 | ...state,
56 | loading: action.payload,
57 | };
58 | case "sliderImages":
59 | return {
60 | ...state,
61 | sliderImages: action.payload,
62 | };
63 | default:
64 | return state;
65 | }
66 | };
67 |
--------------------------------------------------------------------------------
/client/src/components/shop/home/Mixins.js:
--------------------------------------------------------------------------------
1 | export const isWish = (id, wList) => {
2 | if (wList !== null && wList.includes(id) === true) {
3 | return true;
4 | }
5 | return false;
6 | };
7 |
8 | export const isWishReq = (e, id, setWlist) => {
9 | let list = localStorage.getItem("wishList")
10 | ? JSON.parse(localStorage.getItem("wishList"))
11 | : [];
12 | if (list.length > 0) {
13 | if (list.includes(id) !== true) {
14 | list.push(id);
15 | localStorage.setItem("wishList", JSON.stringify(list));
16 | setWlist(list);
17 | }
18 | } else {
19 | list.push(id);
20 | localStorage.setItem("wishList", JSON.stringify(list));
21 | setWlist(list);
22 | }
23 | };
24 |
25 | export const unWishReq = (e, id, setWlist) => {
26 | let list = localStorage.getItem("wishList")
27 | ? JSON.parse(localStorage.getItem("wishList"))
28 | : [];
29 | if (list.length > 0) {
30 | if (list.includes(id) === true) {
31 | list.splice(list.indexOf(id), 1);
32 | localStorage.setItem("wishList", JSON.stringify(list));
33 | setWlist(list);
34 | }
35 | }
36 | };
37 |
38 | export const nextSlide = (totalImg, slide, setSlide) => {
39 | if (slide === totalImg - 1) {
40 | setSlide(0);
41 | } else if (slide < totalImg) {
42 | setSlide(slide + 1);
43 | }
44 | };
45 |
46 | export const prevSlide = (totalImg, slide, setSlide) => {
47 | if (slide === 0) {
48 | setSlide(totalImg - 1);
49 | } else if (slide === totalImg - 1) {
50 | setSlide(0);
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/client/src/components/shop/home/OrderSuccessMessage.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from "react";
2 | import { LayoutContext } from "../layout";
3 |
4 | const OrderSuccessMessage = (props) => {
5 | const { data, dispatch } = useContext(LayoutContext);
6 | return (
7 |
8 |
13 |
14 | Your Order in process. Wait 2 days to deliver.
15 |
16 |
dispatch({ type: "orderSuccess", payload: false })}
18 | className="hover:bg-gray-400 hover:text-gray-800 p-2 rounded-full cursor-pointer"
19 | >
20 |
26 |
31 |
32 |
33 |
34 |
35 | );
36 | };
37 |
38 | export default OrderSuccessMessage;
39 |
--------------------------------------------------------------------------------
/client/src/components/shop/home/ProductByCategory.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect, useState } from "react";
2 | import { useHistory, useParams } from "react-router-dom";
3 | import Layout from "../layout";
4 | import { productByCategory } from "../../admin/products/FetchApi";
5 |
6 | const apiURL = process.env.REACT_APP_API_URL;
7 |
8 | const Submenu = ({ category }) => {
9 | const history = useHistory();
10 | return (
11 |
12 | {/* Submenu Section */}
13 |
14 |
15 |
16 | history.push("/")}
19 | >
20 | Shop
21 |
22 | {category}
23 |
24 |
40 |
41 |
42 | {/* Submenu Section */}
43 |
44 | );
45 | };
46 |
47 | const AllProduct = ({ products }) => {
48 | const history = useHistory();
49 | const category =
50 | products && products.length > 0 ? products[0].pCategory.cName : "";
51 | return (
52 |
53 |
54 |
55 | {products && products.length > 0 ? (
56 | products.map((item, index) => {
57 | return (
58 |
59 |
60 |
history.push(`/products/${item._id}`)}
62 | className="w-full object-cover object-center cursor-pointer"
63 | src={`${apiURL}/uploads/products/${item.pImages[0]}`}
64 | alt=""
65 | />
66 |
67 |
68 | {item.pName}
69 |
70 |
71 |
72 |
79 |
85 |
86 |
87 |
88 | {item.pRatings ? item.pRatings.length : 0}
89 |
90 |
91 |
92 |
{item.pPrice}.00$
93 |
109 |
110 |
111 | );
112 | })
113 | ) : (
114 |
115 | No product found
116 |
117 | )}
118 |
119 |
120 | );
121 | };
122 |
123 | const PageComponent = () => {
124 | const [products, setProducts] = useState(null);
125 | const { catId } = useParams();
126 |
127 | useEffect(() => {
128 | fetchData();
129 | // eslint-disable-next-line react-hooks/exhaustive-deps
130 | }, []);
131 |
132 | const fetchData = async () => {
133 | try {
134 | let responseData = await productByCategory(catId);
135 | if (responseData && responseData.Products) {
136 | setProducts(responseData.Products);
137 | }
138 | } catch (error) {
139 | console.log(error);
140 | }
141 | };
142 |
143 | return (
144 |
145 |
146 |
147 | );
148 | };
149 |
150 | const ProductByCategory = (props) => {
151 | return (
152 |
153 | } />
154 |
155 | );
156 | };
157 |
158 | export default ProductByCategory;
159 |
--------------------------------------------------------------------------------
/client/src/components/shop/home/ProductCategory.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext } from "react";
2 | import ProductCategoryDropdown from "./ProductCategoryDropdown";
3 | import { HomeContext } from "./index";
4 |
5 | const ProductCategory = (props) => {
6 | const { data, dispatch } = useContext(HomeContext);
7 |
8 | return (
9 |
10 |
11 |
13 | dispatch({
14 | type: "categoryListDropdown",
15 | payload: !data.categoryListDropdown,
16 | })
17 | }
18 | className={`flex items-center space-x-1 cursor-pointer ${
19 | data.categoryListDropdown ? "text-yellow-700" : ""
20 | }`}
21 | >
22 |
23 | Categories
24 |
25 |
32 |
38 |
39 |
40 |
41 |
43 | dispatch({
44 | type: "filterListDropdown",
45 | payload: !data.filterListDropdown,
46 | })
47 | }
48 | className={`flex items-center space-x-1 cursor-pointer ${
49 | data.filterListDropdown ? "text-yellow-700" : ""
50 | }`}
51 | >
52 |
Filter
53 |
54 |
61 |
67 |
68 |
69 |
70 |
/
71 |
73 | dispatch({
74 | type: "searchDropdown",
75 | payload: !data.searchDropdown,
76 | })
77 | }
78 | className={`flex items-center space-x-1 cursor-pointer ${
79 | data.searchDropdown ? "text-yellow-700" : ""
80 | }`}
81 | >
82 |
Search
83 |
84 |
91 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | );
105 | };
106 |
107 | export default ProductCategory;
108 |
--------------------------------------------------------------------------------
/client/src/components/shop/home/SingleProduct.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useEffect, useContext } from "react";
2 | import { useHistory } from "react-router-dom";
3 | import { getAllProduct } from "../../admin/products/FetchApi";
4 | import { HomeContext } from "./index";
5 | import { isWishReq, unWishReq, isWish } from "./Mixins";
6 |
7 | const apiURL = process.env.REACT_APP_API_URL;
8 |
9 | const SingleProduct = (props) => {
10 | const { data, dispatch } = useContext(HomeContext);
11 | const { products } = data;
12 | const history = useHistory();
13 |
14 | /* WhisList State */
15 | const [wList, setWlist] = useState(
16 | JSON.parse(localStorage.getItem("wishList"))
17 | );
18 |
19 | useEffect(() => {
20 | fetchData();
21 | // eslint-disable-next-line react-hooks/exhaustive-deps
22 | }, []);
23 |
24 | const fetchData = async () => {
25 | dispatch({ type: "loading", payload: true });
26 | try {
27 | let responseData = await getAllProduct();
28 | setTimeout(() => {
29 | if (responseData && responseData.Products) {
30 | dispatch({ type: "setProducts", payload: responseData.Products });
31 | dispatch({ type: "loading", payload: false });
32 | }
33 | }, 500);
34 | } catch (error) {
35 | console.log(error);
36 | }
37 | };
38 |
39 | if (data.loading) {
40 | return (
41 |
57 | );
58 | }
59 | return (
60 |
61 | {products && products.length > 0 ? (
62 | products.map((item, index) => {
63 | return (
64 |
65 |
66 |
history.push(`/products/${item._id}`)}
68 | className="w-full object-cover object-center cursor-pointer"
69 | src={`${apiURL}/uploads/products/${item.pImages[0]}`}
70 | alt=""
71 | />
72 |
73 |
74 | {item.pName}
75 |
76 |
77 |
78 |
85 |
91 |
92 |
93 |
94 | {item.pRatingsReviews.length}
95 |
96 |
97 |
98 |
${item.pPrice}.00
99 | {/* WhisList Logic */}
100 |
101 |
isWishReq(e, item._id, setWlist)}
103 | className={`${
104 | isWish(item._id, wList) && "hidden"
105 | } w-5 h-5 md:w-6 md:h-6 cursor-pointer text-yellow-700 transition-all duration-300 ease-in`}
106 | fill="none"
107 | stroke="currentColor"
108 | viewBox="0 0 24 24"
109 | xmlns="http://www.w3.org/2000/svg"
110 | >
111 |
117 |
118 |
unWishReq(e, item._id, setWlist)}
120 | className={`${
121 | !isWish(item._id, wList) && "hidden"
122 | } w-5 h-5 md:w-6 md:h-6 cursor-pointer text-yellow-700 transition-all duration-300 ease-in`}
123 | fill="currentColor"
124 | viewBox="0 0 20 20"
125 | xmlns="http://www.w3.org/2000/svg"
126 | >
127 |
132 |
133 |
134 | {/* WhisList Logic End */}
135 |
136 |
137 | );
138 | })
139 | ) : (
140 |
141 | No product found
142 |
143 | )}
144 |
145 | );
146 | };
147 |
148 | export default SingleProduct;
149 |
--------------------------------------------------------------------------------
/client/src/components/shop/home/Slider.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect, useContext, useState } from "react";
2 | import OrderSuccessMessage from "./OrderSuccessMessage";
3 | import { HomeContext } from "./";
4 | import { sliderImages } from "../../admin/dashboardAdmin/Action";
5 | import { prevSlide, nextSlide } from "./Mixins";
6 |
7 | const apiURL = process.env.REACT_APP_API_URL;
8 |
9 | const Slider = (props) => {
10 | const { data, dispatch } = useContext(HomeContext);
11 | const [slide, setSlide] = useState(0);
12 |
13 | useEffect(() => {
14 | sliderImages(dispatch);
15 | // eslint-disable-next-line react-hooks/exhaustive-deps
16 | }, []);
17 |
18 | return (
19 |
20 |
21 | {data.sliderImages.length > 0 ? (
22 |
27 | ) : (
28 | ""
29 | )}
30 |
31 | {data?.sliderImages?.length > 0 ? (
32 | <>
33 |
35 | prevSlide(data.sliderImages.length, slide, setSlide)
36 | }
37 | className={`z-10 absolute top-0 left-0 mt-64 flex justify-end items-center box-border flex justify-center w-12 h-12 text-gray-700 cursor-pointer hover:text-yellow-700`}
38 | fill="none"
39 | stroke="currentColor"
40 | viewBox="0 0 24 24"
41 | xmlns="http://www.w3.org/2000/svg"
42 | >
43 |
49 |
50 |
52 | nextSlide(data.sliderImages.length, slide, setSlide)
53 | }
54 | className={`z-10 absolute top-0 right-0 mt-64 flex justify-start items-center box-border flex justify-center w-12 h-12 text-gray-700 cursor-pointer hover:text-yellow-700`}
55 | fill="none"
56 | stroke="currentColor"
57 | viewBox="0 0 24 24"
58 | xmlns="http://www.w3.org/2000/svg"
59 | >
60 |
66 |
67 |
76 | >
77 | ) : null}
78 |
79 |
80 |
81 | );
82 | };
83 |
84 | export default Slider;
85 |
--------------------------------------------------------------------------------
/client/src/components/shop/home/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, createContext, useReducer } from "react";
2 | import Layout from "../layout";
3 | import Slider from "./Slider";
4 | import ProductCategory from "./ProductCategory";
5 | import { homeState, homeReducer } from "./HomeContext";
6 | import SingleProduct from "./SingleProduct";
7 |
8 | export const HomeContext = createContext();
9 |
10 | const HomeComponent = () => {
11 | return (
12 |
13 |
14 | {/* Category, Search & Filter Section */}
15 |
18 | {/* Product Section */}
19 |
22 |
23 | );
24 | };
25 |
26 | const Home = (props) => {
27 | const [data, dispatch] = useReducer(homeReducer, homeState);
28 | return (
29 |
30 |
31 | } />
32 |
33 |
34 | );
35 | };
36 |
37 | export default Home;
38 |
--------------------------------------------------------------------------------
/client/src/components/shop/home/style.css:
--------------------------------------------------------------------------------
1 | .slider {
2 | -webkit-appearance: none;
3 | height: 12px;
4 | border-radius: 5px;
5 | background: #d3d3d3;
6 | outline: none;
7 | -webkit-transition: 0.2s;
8 | transition: opacity 0.2s;
9 | }
10 |
11 | .slider::-webkit-slider-thumb {
12 | -webkit-appearance: none;
13 | appearance: none;
14 | width: 18px;
15 | height: 18px;
16 | border-radius: 75%;
17 | background: #b7791f;
18 | cursor: pointer;
19 | }
20 |
21 | .slider::-moz-range-thumb {
22 | width: 18px;
23 | height: 18px;
24 | border-radius: 75%;
25 | background: #b7791f;
26 | cursor: pointer;
27 | }
28 |
--------------------------------------------------------------------------------
/client/src/components/shop/index.js:
--------------------------------------------------------------------------------
1 | import Home from "./home";
2 | import WishList from "./wishlist";
3 | import ProtectedRoute from "./auth/ProtectedRoute";
4 | import AdminProtectedRoute from "./auth/AdminProtectedRoute";
5 | import CartProtectedRoute from "./auth/CartProtectedRoute";
6 | import { LayoutContext } from "./layout";
7 | import { layoutState, layoutReducer } from "./layout/layoutContext";
8 | import { isAdmin, isAuthenticate } from "./auth/fetchApi";
9 | import PageNotFound from "./layout/PageNotFound";
10 | import ProductDetails from "./productDetails";
11 | import ProductByCategory from "./home/ProductByCategory";
12 | import CheckoutPage from "./order/CheckoutPage";
13 |
14 | export {
15 | Home,
16 | WishList,
17 | ProtectedRoute,
18 | AdminProtectedRoute,
19 | CartProtectedRoute,
20 | LayoutContext,
21 | layoutState,
22 | layoutReducer,
23 | isAdmin,
24 | isAuthenticate,
25 | PageNotFound,
26 | ProductDetails,
27 | ProductByCategory,
28 | CheckoutPage,
29 | };
30 |
--------------------------------------------------------------------------------
/client/src/components/shop/layout/PageNotFound.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Layout from "./index";
3 |
4 | const PageNotFoundComponent = (props) => {
5 | return (
6 |
7 |
8 |
15 |
21 |
22 |
23 |
24 | 404 not found
25 |
26 |
27 | );
28 | };
29 |
30 | const PageNotFound = (props) => {
31 | return } />;
32 | };
33 |
34 | export default PageNotFound;
35 |
--------------------------------------------------------------------------------
/client/src/components/shop/layout/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, createContext } from "react";
2 | import { Navber, Footer, CartModal } from "../partials";
3 | import LoginSignup from "../auth/LoginSignup";
4 |
5 | export const LayoutContext = createContext();
6 |
7 | const Layout = ({ children }) => {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 | {/* All Children pass from here */}
15 | {children}
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default Layout;
23 |
--------------------------------------------------------------------------------
/client/src/components/shop/layout/layoutContext.js:
--------------------------------------------------------------------------------
1 | export const layoutState = {
2 | navberHamburger: false,
3 | loginSignupModal: false,
4 | loginSignupError: false,
5 | cartModal: false,
6 | cartProduct: null,
7 | singleProductDetail: null,
8 | inCart: null,
9 | cartTotalCost: null,
10 | orderSuccess: false,
11 | loading: false,
12 | };
13 |
14 | export const layoutReducer = (state, action) => {
15 | switch (action.type) {
16 | case "hamburgerToggle":
17 | return {
18 | ...state,
19 | navberHamburger: action.payload,
20 | };
21 | case "loginSignupModalToggle":
22 | return {
23 | ...state,
24 | loginSignupModal: action.payload,
25 | };
26 | case "cartModalToggle":
27 | return {
28 | ...state,
29 | cartModal: action.payload,
30 | };
31 | case "cartProduct":
32 | return {
33 | ...state,
34 | cartProduct: action.payload,
35 | };
36 | case "singleProductDetail":
37 | return {
38 | ...state,
39 | singleProductDetail: action.payload,
40 | };
41 | case "inCart":
42 | return {
43 | ...state,
44 | inCart: action.payload,
45 | };
46 | case "cartTotalCost":
47 | return {
48 | ...state,
49 | cartTotalCost: action.payload,
50 | };
51 | case "loginSignupError":
52 | return {
53 | ...state,
54 | loginSignupError: action.payload,
55 | };
56 | case "orderSuccess":
57 | return {
58 | ...state,
59 | orderSuccess: action.payload,
60 | };
61 | case "loading":
62 | return {
63 | ...state,
64 | loading: action.payload,
65 | };
66 | default:
67 | return state;
68 | }
69 | };
70 |
--------------------------------------------------------------------------------
/client/src/components/shop/order/Action.js:
--------------------------------------------------------------------------------
1 | import { createOrder } from "./FetchApi";
2 |
3 | export const fetchData = async (cartListProduct, dispatch) => {
4 | dispatch({ type: "loading", payload: true });
5 | try {
6 | let responseData = await cartListProduct();
7 | if (responseData && responseData.Products) {
8 | setTimeout(function () {
9 | dispatch({ type: "cartProduct", payload: responseData.Products });
10 | dispatch({ type: "loading", payload: false });
11 | }, 1000);
12 | }
13 | } catch (error) {
14 | console.log(error);
15 | }
16 | };
17 |
18 | export const fetchbrainTree = async (getBrainTreeToken, setState) => {
19 | try {
20 | let responseData = await getBrainTreeToken();
21 | if (responseData && responseData) {
22 | setState({
23 | clientToken: responseData.clientToken,
24 | success: responseData.success,
25 | });
26 | console.log(responseData);
27 | }
28 | } catch (error) {
29 | console.log(error);
30 | }
31 | };
32 |
33 | export const pay = async (
34 | data,
35 | dispatch,
36 | state,
37 | setState,
38 | getPaymentProcess,
39 | totalCost,
40 | history
41 | ) => {
42 | console.log(state);
43 | if (!state.address) {
44 | setState({ ...state, error: "Please provide your address" });
45 | } else if (!state.phone) {
46 | setState({ ...state, error: "Please provide your phone number" });
47 | } else {
48 | let nonce;
49 | state.instance
50 | .requestPaymentMethod()
51 | .then((data) => {
52 | dispatch({ type: "loading", payload: true });
53 | nonce = data.nonce;
54 | let paymentData = {
55 | amountTotal: totalCost(),
56 | paymentMethod: nonce,
57 | };
58 | getPaymentProcess(paymentData)
59 | .then(async (res) => {
60 | if (res) {
61 | let orderData = {
62 | allProduct: JSON.parse(localStorage.getItem("cart")),
63 | user: JSON.parse(localStorage.getItem("jwt")).user._id,
64 | amount: res.transaction.amount,
65 | transactionId: res.transaction.id,
66 | address: state.address,
67 | phone: state.phone,
68 | };
69 | try {
70 | let resposeData = await createOrder(orderData);
71 | if (resposeData.success) {
72 | localStorage.setItem("cart", JSON.stringify([]));
73 | dispatch({ type: "cartProduct", payload: null });
74 | dispatch({ type: "cartTotalCost", payload: null });
75 | dispatch({ type: "orderSuccess", payload: true });
76 | setState({ clientToken: "", instance: {} });
77 | dispatch({ type: "loading", payload: false });
78 | return history.push("/");
79 | } else if (resposeData.error) {
80 | console.log(resposeData.error);
81 | }
82 | } catch (error) {
83 | console.log(error);
84 | }
85 | }
86 | })
87 | .catch((err) => {
88 | console.log(err);
89 | });
90 | })
91 | .catch((error) => {
92 | console.log(error);
93 | setState({ ...state, error: error.message });
94 | });
95 | }
96 | };
97 |
--------------------------------------------------------------------------------
/client/src/components/shop/order/CheckoutPage.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import Layout from "../layout";
3 | import { CheckoutComponent } from "./CheckoutProducts";
4 |
5 | const CheckoutPage = (props) => {
6 | return (
7 |
8 | } />
9 |
10 | );
11 | };
12 |
13 | export default CheckoutPage;
14 |
--------------------------------------------------------------------------------
/client/src/components/shop/order/FetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | export const getBrainTreeToken = async () => {
5 | let uId = JSON.parse(localStorage.getItem("jwt")).user._id;
6 | try {
7 | let res = await axios.post(`${apiURL}/api/braintree/get-token`, {
8 | uId: uId,
9 | });
10 | return res.data;
11 | } catch (error) {
12 | console.log(error);
13 | }
14 | };
15 |
16 | export const getPaymentProcess = async (paymentData) => {
17 | try {
18 | let res = await axios.post(`${apiURL}/api/braintree/payment`, paymentData);
19 | return res.data;
20 | } catch (error) {
21 | console.log(error);
22 | }
23 | };
24 |
25 | export const createOrder = async (orderData) => {
26 | try {
27 | let res = await axios.post(`${apiURL}/api/order/create-order`, orderData);
28 | return res.data;
29 | } catch (error) {
30 | console.log(error);
31 | }
32 | };
33 |
--------------------------------------------------------------------------------
/client/src/components/shop/partials/Action.js:
--------------------------------------------------------------------------------
1 | export const logout = () => {
2 | localStorage.removeItem("jwt");
3 | localStorage.removeItem("cart");
4 | localStorage.removeItem("wishList");
5 | window.location.href = "/";
6 | };
7 |
--------------------------------------------------------------------------------
/client/src/components/shop/partials/FetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | export const cartListProduct = async () => {
5 | let carts = JSON.parse(localStorage.getItem("cart"));
6 | let productArray = [];
7 | if (carts) {
8 | for (const cart of carts) {
9 | productArray.push(cart.id);
10 | }
11 | }
12 | try {
13 | let res = await axios.post(`${apiURL}/api/product/cart-product`, {
14 | productArray,
15 | });
16 | return res.data;
17 | } catch (error) {
18 | console.log(error);
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/client/src/components/shop/partials/Footer.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import moment from "moment";
3 |
4 | const Footer = (props) => {
5 | return (
6 |
7 |
11 | Develop & Design Hasan-py © Copyright {moment().format("YYYY")}
12 |
13 |
14 | );
15 | };
16 |
17 | export default Footer;
18 |
--------------------------------------------------------------------------------
/client/src/components/shop/partials/Mixins.js:
--------------------------------------------------------------------------------
1 | export const subTotal = (id, price) => {
2 | let subTotalCost = 0;
3 | let carts = JSON.parse(localStorage.getItem("cart"));
4 | carts.forEach((item) => {
5 | if (item.id === id) {
6 | subTotalCost = item.quantitiy * price;
7 | }
8 | });
9 | return subTotalCost;
10 | };
11 |
12 | export const quantity = (id) => {
13 | let product = 0;
14 | let carts = JSON.parse(localStorage.getItem("cart"));
15 | carts.forEach((item) => {
16 | if (item.id === id) {
17 | product = item.quantitiy;
18 | }
19 | });
20 | return product;
21 | };
22 |
23 | export const totalCost = () => {
24 | let totalCost = 0;
25 | let carts = JSON.parse(localStorage.getItem("cart"));
26 | carts.forEach((item) => {
27 | totalCost += item.quantitiy * item.price;
28 | });
29 | return totalCost;
30 | };
31 |
--------------------------------------------------------------------------------
/client/src/components/shop/partials/index.js:
--------------------------------------------------------------------------------
1 | import Navber from "./Navber";
2 | import Footer from "./Footer";
3 | import CartModal from "./CartModal";
4 |
5 | export { Navber, Footer, CartModal };
6 |
--------------------------------------------------------------------------------
/client/src/components/shop/partials/style.css:
--------------------------------------------------------------------------------
1 | .userDropdown {
2 | display: none;
3 | }
4 |
5 | .userDropdownBtn:hover .userDropdown {
6 | display: block;
7 | display: flex;
8 | flex-direction: column;
9 | transition: all 1s;
10 | }
11 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/Action.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { postAddReview, postDeleteReview } from "./FetchApi";
3 | import { isAuthenticate } from "../auth/fetchApi";
4 |
5 | export const Alert = (color, text) => (
6 | {text}
7 | );
8 |
9 | export const reviewSubmitHanlder = (fData, setFdata, fetchData) => {
10 | if (!fData.rating || !fData.review) {
11 | setFdata({ ...fData, error: "Rating and review must be required" });
12 | } else if (!isAuthenticate()) {
13 | setFdata({ ...fData, error: "You must need login to review" });
14 | } else {
15 | addReview(fData, setFdata, fetchData);
16 | }
17 | };
18 |
19 | export const deleteReview = async (
20 | reviewId,
21 | productId,
22 | fetchData,
23 | setFdata
24 | ) => {
25 | try {
26 | let responseData = await postDeleteReview({
27 | rId: reviewId,
28 | pId: productId,
29 | });
30 | if (responseData.success) {
31 | fetchData();
32 | setFdata({ success: responseData.success });
33 | } else if (responseData.error) {
34 | fetchData();
35 | }
36 | } catch (error) {
37 | console.log(error);
38 | }
39 | };
40 |
41 | export const addReview = async (fData, setFdata, fetchData) => {
42 | let formData = {
43 | rating: fData.rating,
44 | review: fData.review,
45 | pId: fData.pId,
46 | uId: JSON.parse(localStorage.getItem("jwt")).user._id,
47 | };
48 | try {
49 | let responseData = await postAddReview(formData);
50 | if (responseData.success) {
51 | setFdata({
52 | ...fData,
53 | success: responseData.success,
54 | review: "",
55 | rating: "",
56 | });
57 | fetchData();
58 | } else if (responseData.error) {
59 | setFdata({ ...fData, error: responseData.error, review: "", rating: "" });
60 | fetchData();
61 | }
62 | } catch (error) {
63 | console.log(error);
64 | }
65 | };
66 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/Details.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import ProductDetailsSection from "./ProductDetailsSection";
3 |
4 | const Details = (props) => {
5 | return (
6 |
7 |
8 |
9 | );
10 | };
11 |
12 | export default Details;
13 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/FetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | export const getSingleProduct = async (pId) => {
5 | try {
6 | let res = await axios.post(`${apiURL}/api/product/single-product`, {
7 | pId: pId,
8 | });
9 | return res.data;
10 | } catch (error) {
11 | console.log(error);
12 | }
13 | };
14 |
15 | export const postAddReview = async (formData) => {
16 | try {
17 | let res = await axios.post(`${apiURL}/api/product/add-review`, formData);
18 | return res.data;
19 | } catch (error) {
20 | console.log(error);
21 | }
22 | };
23 |
24 | export const postDeleteReview = async (formData) => {
25 | try {
26 | let res = await axios.post(`${apiURL}/api/product/delete-review`, formData);
27 | return res.data;
28 | } catch (error) {
29 | console.log(error);
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/Mixins.js:
--------------------------------------------------------------------------------
1 | export const cartList = () => {
2 | let carts = localStorage.getItem("cart")
3 | ? JSON.parse(localStorage.getItem("cart"))
4 | : null;
5 | let list = [];
6 | if (carts !== null) {
7 | for (let cart of carts) {
8 | list.push(cart.id);
9 | }
10 | return list;
11 | } else {
12 | return (list = null);
13 | }
14 | };
15 |
16 | export const updateQuantity = (
17 | type,
18 | totalQuantitiy,
19 | quantitiy,
20 | setQuantitiy,
21 | setAlertq
22 | ) => {
23 | if (type === "increase") {
24 | if (quantitiy === totalQuantitiy) {
25 | setAlertq(true);
26 | } else {
27 | setQuantitiy(quantitiy + 1);
28 | }
29 | } else if (type === "decrease") {
30 | if (quantitiy === 1) {
31 | setQuantitiy(1);
32 | setAlertq(false);
33 | } else {
34 | setQuantitiy(quantitiy - 1);
35 | }
36 | }
37 | };
38 |
39 | export const slideImage = (type, active, count, setCount, pImages) => {
40 | if (active === count) {
41 | return true;
42 | }
43 | if (type === "increase") {
44 | if (count === pImages.length - 1) {
45 | setCount(0);
46 | } else if (count < pImages.length) {
47 | setCount(count + 1);
48 | }
49 | }
50 | };
51 |
52 | export const inCart = (id) => {
53 | if (localStorage.getItem("cart")) {
54 | let cartProducts = JSON.parse(localStorage.getItem("cart"));
55 | for (let product of cartProducts) {
56 | if (product.id === id) {
57 | return true;
58 | }
59 | }
60 | }
61 | return false;
62 | };
63 |
64 | export const addToCart = (
65 | id,
66 | quantitiy,
67 | price,
68 | layoutDispatch,
69 | setQuantitiy,
70 | setAlertq,
71 | fetchData,
72 | totalCost
73 | ) => {
74 | let isObj = false;
75 | let cart = localStorage.getItem("cart")
76 | ? JSON.parse(localStorage.getItem("cart"))
77 | : [];
78 | if (cart.length > 0) {
79 | cart.forEach((item) => {
80 | if (item.id === id) {
81 | isObj = true;
82 | }
83 | });
84 | if (!isObj) {
85 | cart.push({ id, quantitiy, price });
86 | localStorage.setItem("cart", JSON.stringify(cart));
87 | }
88 | } else {
89 | cart.push({ id, quantitiy, price });
90 | localStorage.setItem("cart", JSON.stringify(cart));
91 | }
92 | layoutDispatch({ type: "inCart", payload: cartList() });
93 | layoutDispatch({ type: "cartTotalCost", payload: totalCost() });
94 | setQuantitiy(1);
95 | setAlertq(false);
96 | fetchData();
97 | };
98 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/ProductDetailsContext.js:
--------------------------------------------------------------------------------
1 | export const productDetailsState = {
2 | loading: false,
3 | menu: true,
4 | };
5 |
6 | export const productDetailsReducer = (state, action) => {
7 | switch (action.type) {
8 | case "menu":
9 | return {
10 | ...state,
11 | menu: action.payload,
12 | };
13 | case "loading":
14 | return {
15 | ...state,
16 | loading: action.payload,
17 | };
18 | case "cartState":
19 | return {
20 | ...state,
21 | cartState: action.payload,
22 | };
23 | default:
24 | return state;
25 | }
26 | };
27 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/ProductDetailsSectionTwo.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useContext, useEffect, useState } from "react";
2 | import AllReviews from "./AllReviews";
3 | import ReviewForm from "./ReviewForm";
4 |
5 | import { ProductDetailsContext } from "./";
6 | import { LayoutContext } from "../layout";
7 |
8 | import { isAuthenticate } from "../auth/fetchApi";
9 |
10 | import "./style.css";
11 |
12 | const Menu = () => {
13 | const { data, dispatch } = useContext(ProductDetailsContext);
14 | const { data: layoutData } = useContext(LayoutContext);
15 |
16 | return (
17 |
18 |
19 |
dispatch({ type: "menu", payload: true })}
21 | className={`${
22 | data.menu ? "border-b-2 border-yellow-700" : ""
23 | } px-4 py-3 cursor-pointer`}
24 | >
25 | Description
26 |
27 |
dispatch({ type: "menu", payload: false })}
29 | className={`${
30 | !data.menu ? "border-b-2 border-yellow-700" : ""
31 | } px-4 py-3 relative flex cursor-pointer`}
32 | >
33 | Reviews
34 |
35 | {layoutData.singleProductDetail.pRatingsReviews.length}
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | const RatingReview = () => {
44 | return (
45 |
46 |
47 | {isAuthenticate() ? (
48 |
49 | ) : (
50 |
51 | You need to login in for review
52 |
53 | )}
54 |
55 | );
56 | };
57 |
58 | const ProductDetailsSectionTwo = (props) => {
59 | const { data } = useContext(ProductDetailsContext);
60 | const { data: layoutData } = useContext(LayoutContext);
61 | const [singleProduct, setSingleproduct] = useState({});
62 |
63 | useEffect(() => {
64 | setSingleproduct(
65 | layoutData.singleProductDetail ? layoutData.singleProductDetail : ""
66 | );
67 |
68 | // eslint-disable-next-line react-hooks/exhaustive-deps
69 | }, []);
70 |
71 | return (
72 |
73 |
74 |
75 | {data.menu ? (
76 | {singleProduct.pDescription}
77 | ) : (
78 |
79 | )}
80 |
81 |
82 |
83 | Category :
84 |
85 | {" "}
86 | {singleProduct.pCategory ? singleProduct.pCategory.cName : ""}
87 |
88 |
89 |
90 |
91 | );
92 | };
93 |
94 | export default ProductDetailsSectionTwo;
95 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/ReviewForm.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useState, useContext } from "react";
2 | import { useParams } from "react-router-dom";
3 | import { Alert, reviewSubmitHanlder } from "./Action";
4 | import { LayoutContext } from "../layout";
5 | import { isAuthenticate } from "../auth/fetchApi";
6 | import { getSingleProduct } from "./FetchApi";
7 |
8 | const ReviewForm = (props) => {
9 | const { data, dispatch } = useContext(LayoutContext);
10 | let { id } = useParams(); // Product Id
11 |
12 | const [fData, setFdata] = useState({
13 | rating: "",
14 | review: "",
15 | error: false,
16 | success: false,
17 | pId: id,
18 | });
19 |
20 | if (fData.error || fData.success) {
21 | setTimeout(() => {
22 | setFdata({ ...fData, error: false, success: false });
23 | }, 3000);
24 | }
25 |
26 | const fetchData = async () => {
27 | try {
28 | let responseData = await getSingleProduct(id);
29 | if (responseData.Product) {
30 | dispatch({
31 | type: "singleProductDetail",
32 | payload: responseData.Product,
33 | });
34 | console.log(data);
35 | }
36 | if (responseData.error) {
37 | console.log(responseData.error);
38 | }
39 | } catch (error) {
40 | console.log(error);
41 | }
42 | };
43 |
44 | const ratingUserList = data.singleProductDetail.pRatingsReviews.map(
45 | (item) => {
46 | return item.user ? item.user._id : "";
47 | }
48 | );
49 |
50 | return (
51 |
52 |
53 | {fData.error ? Alert("red", fData.error) : ""}
54 | {fData.success ? Alert("green", fData.success) : ""}
55 |
56 | {ratingUserList.includes(isAuthenticate().user._id) ? (
57 |
58 | ) : (
59 |
157 | )}
158 |
159 | );
160 | };
161 |
162 | export default ReviewForm;
163 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/Submenu.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import { useHistory } from "react-router-dom";
3 |
4 | const Submenu = (props) => {
5 | const { categoryId, category, product } = props.value;
6 | const history = useHistory();
7 | return (
8 |
9 | {/* Submenu Section */}
10 |
11 |
12 |
13 | history.push("/")}
16 | >
17 | Shop
18 |
19 | history.push(`/products/category/${categoryId}`)}
22 | >
23 | {category}
24 |
25 | {product}
26 |
27 |
43 |
44 |
45 | {/* Submenu Section */}
46 |
47 | );
48 | };
49 |
50 | export default Submenu;
51 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, createContext, useReducer } from "react";
2 | import Layout from "../layout";
3 | import {
4 | productDetailsState,
5 | productDetailsReducer,
6 | } from "./ProductDetailsContext";
7 | import Details from "./Details";
8 |
9 | export const ProductDetailsContext = createContext();
10 |
11 | const DetailsComponent = () => {
12 | return (
13 |
14 |
15 |
16 | );
17 | };
18 |
19 | const ProductDetails = (props) => {
20 | const [data, dispatch] = useReducer(
21 | productDetailsReducer,
22 | productDetailsState
23 | );
24 | return (
25 |
26 |
27 | } />
28 |
29 |
30 | );
31 | };
32 |
33 | export default ProductDetails;
34 |
--------------------------------------------------------------------------------
/client/src/components/shop/productDetails/style.css:
--------------------------------------------------------------------------------
1 | @import url(//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css);
2 |
3 | fieldset,
4 | label {
5 | margin: 0;
6 | padding: 0;
7 | }
8 |
9 | /****** Style Star Rating Widget *****/
10 |
11 | .rating {
12 | border: none;
13 | float: left;
14 | }
15 |
16 | .rating > input {
17 | display: none;
18 | }
19 |
20 | .rating > label:before {
21 | margin: 5px;
22 | font-size: 2.25em;
23 | font-family: FontAwesome;
24 | display: inline-block;
25 | content: "\f005";
26 | }
27 |
28 | /* Half Rating System */
29 |
30 | /*.rating > .half:before {
31 | content: "\f089";
32 | position: absolute;
33 | }
34 | */
35 |
36 | /*
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | */
47 |
48 | .rating > label {
49 | color: #ddd;
50 | float: right;
51 | }
52 |
53 | /***** CSS Magic to Highlight Stars on Hover *****/
54 |
55 | .rating>input:checked~label,
56 | /* show gold star when clicked */
57 | .rating:not(:checked)>label:hover,
58 | /* hover current star */
59 | .rating:not(:checked)>label:hover~label {
60 | color: #b7791f;
61 | }
62 |
63 | /* hover previous stars in list */
64 |
65 | .rating>input:checked+label:hover,
66 | /* hover current star when changing rating */
67 | .rating>input:checked~label:hover,
68 | .rating>label:hover~input:checked~label,
69 | /* lighten current selection */
70 | .rating>input:checked~label:hover~label {
71 | color: #b7791f;
72 | }
73 |
--------------------------------------------------------------------------------
/client/src/components/shop/wishlist/FetchApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | const apiURL = process.env.REACT_APP_API_URL;
3 |
4 | export const wishListProducts = async () => {
5 | let productArray = JSON.parse(localStorage.getItem("wishList"));
6 | try {
7 | let res = await axios.post(`${apiURL}/api/product/wish-product`, {
8 | productArray,
9 | });
10 | return res.data;
11 | } catch (error) {
12 | console.log(error);
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/client/src/components/shop/wishlist/Mixins.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hasan-py/MERN_Stack_Project_Ecommerce_Hayroo/5fbc0a49a3e08d8186065044c21a1e9aabd7a5c7/client/src/components/shop/wishlist/Mixins.js
--------------------------------------------------------------------------------
/client/src/components/shop/wishlist/SingleWishProduct.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment, useEffect, useState } from "react";
2 | import { useHistory } from "react-router-dom";
3 | import { wishListProducts } from "./FetchApi";
4 | const apiURL = process.env.REACT_APP_API_URL;
5 |
6 | const Product = () => {
7 | const history = useHistory();
8 | const [products, setProducts] = useState([]);
9 | const [loading, setLoading] = useState(false);
10 |
11 | useEffect(() => {
12 | fetchData();
13 | }, []);
14 |
15 | const fetchData = async () => {
16 | setLoading(true);
17 | let responseData = await wishListProducts();
18 | setTimeout(() => {
19 | if (responseData && responseData.Products) {
20 | setProducts(responseData.Products);
21 | setLoading(false);
22 | }
23 | }, 50);
24 | };
25 |
26 | const removeFromWishList = (id) => {
27 | let list = localStorage.getItem("wishList")
28 | ? JSON.parse(localStorage.getItem("wishList"))
29 | : [];
30 | if (list.length > 0) {
31 | if (list.includes(id) === true) {
32 | list.splice(list.indexOf(id), 1);
33 | localStorage.setItem("wishList", JSON.stringify(list));
34 | fetchData();
35 | }
36 | }
37 | };
38 | if (loading) {
39 | return (
40 |
41 | No product found in wishList
42 |
43 | );
44 | }
45 | return (
46 |
47 |
48 | {products.length > 0 ? (
49 | products.map((product, index) => {
50 | return (
51 |
55 |
56 |
history.push(`/products/${product._id}`)}
58 | className="cursor-pointer md:h-20 md:w-20 object-cover object-center"
59 | src={`${apiURL}/uploads/products/${product.pImages[0]}`}
60 | alt="wishListproduct"
61 | />
62 |
63 | {product.pName}
64 |
65 |
66 |
67 |
68 | ${product.pPrice}.00
69 |
70 | {product.pQuantity > 0 ? (
71 |
In Stock
72 | ) : (
73 |
Out Stock
74 | )}
75 |
76 |
history.push(`/products/${product._id}`)}
79 | className="inline-block px-4 py-2 text-white text-xs md:text-base text-center cursor-pointer hover:opacity-75"
80 | >
81 | View
82 |
83 |
84 |
85 |
removeFromWishList(product._id)}
87 | className="w-6 h-6 cursor-pointer"
88 | fill="currentColor"
89 | viewBox="0 0 20 20"
90 | xmlns="http://www.w3.org/2000/svg"
91 | >
92 |
97 |
98 |
99 |
100 | );
101 | })
102 | ) : (
103 |
No product found in wishList
104 | )}
105 |
106 |
107 | );
108 | };
109 |
110 | const SingleWishProduct = (props) => {
111 | return (
112 |
113 |
114 | Wishlist
115 | {/* Product List */}
116 |
117 |
118 |
119 | );
120 | };
121 |
122 | export default SingleWishProduct;
123 |
--------------------------------------------------------------------------------
/client/src/components/shop/wishlist/index.js:
--------------------------------------------------------------------------------
1 | import React, { Fragment } from "react";
2 | import Layout from "../layout";
3 | import SingleWishProduct from "./SingleWishProduct";
4 |
5 | const WishList = () => {
6 | return (
7 |
8 | } />
9 |
10 | );
11 | };
12 |
13 | export default WishList;
14 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import App from "./App";
4 | import * as serviceWorker from "./serviceWorker";
5 | import {SnackbarProvider} from 'notistack'
6 | ReactDOM.render(
7 |
8 |
9 |
10 |
11 | ,
12 | document.getElementById("root")
13 | );
14 |
15 | serviceWorker.unregister();
16 |
--------------------------------------------------------------------------------
/client/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.0/8 are considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl, {
104 | headers: { 'Service-Worker': 'script' },
105 | })
106 | .then(response => {
107 | // Ensure service worker exists, and that we really are getting a JS file.
108 | const contentType = response.headers.get('content-type');
109 | if (
110 | response.status === 404 ||
111 | (contentType != null && contentType.indexOf('javascript') === -1)
112 | ) {
113 | // No service worker found. Probably a different app. Reload the page.
114 | navigator.serviceWorker.ready.then(registration => {
115 | registration.unregister().then(() => {
116 | window.location.reload();
117 | });
118 | });
119 | } else {
120 | // Service worker found. Proceed as normal.
121 | registerValidSW(swUrl, config);
122 | }
123 | })
124 | .catch(() => {
125 | console.log(
126 | 'No internet connection found. App is running in offline mode.'
127 | );
128 | });
129 | }
130 |
131 | export function unregister() {
132 | if ('serviceWorker' in navigator) {
133 | navigator.serviceWorker.ready
134 | .then(registration => {
135 | registration.unregister();
136 | })
137 | .catch(error => {
138 | console.error(error.message);
139 | });
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/server/.env:
--------------------------------------------------------------------------------
1 | DATABASE=mongodb://127.0.0.1:27017/ecommerce
2 | PORT=8000
3 | BRAINTREE_MERCHANT_ID=n74dc2kw9g3ws389
4 | BRAINTREE_PUBLIC_KEY=bgytmgzhz5f6t2tg
5 | BRAINTREE_PRIVATE_KEY=e6f226166da99d874f00008f0bba14fe
6 |
--------------------------------------------------------------------------------
/server/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | public
--------------------------------------------------------------------------------
/server/app.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | ================== Most Important ==================
4 | * Issue 1 :
5 | In uploads folder you need create 3 folder like bellow.
6 | Folder structure will be like:
7 | public -> uploads -> 1. products 2. customize 3. categories
8 | *** Now This folder will automatically create when we run the server file
9 |
10 | * Issue 2:
11 | For admin signup just go to the auth
12 | controller then newUser obj, you will
13 | find a role field. role:1 for admin signup &
14 | role: 0 or by default it for customer signup.
15 | go user model and see the role field.
16 |
17 | */
18 |
19 | const express = require("express");
20 | const app = express();
21 | require("dotenv").config();
22 | const mongoose = require("mongoose");
23 | const morgan = require("morgan");
24 | const cookieParser = require("cookie-parser");
25 | const cors = require("cors");
26 |
27 | // Import Router
28 | const authRouter = require("./routes/auth");
29 | const categoryRouter = require("./routes/categories");
30 | const productRouter = require("./routes/products");
31 | const brainTreeRouter = require("./routes/braintree");
32 | const orderRouter = require("./routes/orders");
33 | const usersRouter = require("./routes/users");
34 | const customizeRouter = require("./routes/customize");
35 | // Import Auth middleware for check user login or not~
36 | const { loginCheck } = require("./middleware/auth");
37 | const CreateAllFolder = require("./config/uploadFolderCreateScript");
38 |
39 | /* Create All Uploads Folder if not exists | For Uploading Images */
40 | CreateAllFolder();
41 |
42 | // Database Connection
43 | mongoose
44 | .connect(process.env.DATABASE, {
45 | useNewUrlParser: true,
46 | useUnifiedTopology: true,
47 | useCreateIndex: true,
48 | })
49 | .then(() =>
50 | console.log(
51 | "==============Mongodb Database Connected Successfully=============="
52 | )
53 | )
54 | .catch((err) => console.log("Database Not Connected !!!"));
55 |
56 | // Middleware
57 | app.use(morgan("dev"));
58 | app.use(cookieParser());
59 | app.use(cors());
60 | app.use(express.static("public"));
61 | app.use(express.urlencoded({ extended: false }));
62 | app.use(express.json());
63 |
64 | // Routes
65 | app.use("/api", authRouter);
66 | app.use("/api/user", usersRouter);
67 | app.use("/api/category", categoryRouter);
68 | app.use("/api/product", productRouter);
69 | app.use("/api", brainTreeRouter);
70 | app.use("/api/order", orderRouter);
71 | app.use("/api/customize", customizeRouter);
72 |
73 | // Run Server
74 | const PORT = process.env.PORT || 8000;
75 | app.listen(PORT, () => {
76 | console.log("Server is running on ", PORT);
77 | });
78 |
--------------------------------------------------------------------------------
/server/config/db.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | try {
3 | mongoose.connect("mongodb://localhost:27017/Ecommerce", {
4 | useNewUrlParser: true,
5 | useUnifiedTopology: true,
6 | useCreateIndex: true,
7 | });
8 | console.log("Database Connected Successfully");
9 | } catch (err) {
10 | console.log("Database Not Connected");
11 | }
12 |
--------------------------------------------------------------------------------
/server/config/function.js:
--------------------------------------------------------------------------------
1 | /* This all of are helper function */
2 | const userModel = require("../models/users");
3 |
4 | exports.toTitleCase = function (str) {
5 | return str.replace(/\w\S*/g, function (txt) {
6 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
7 | });
8 | };
9 |
10 | exports.validateEmail = function (mail) {
11 | if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(mail)) {
12 | return true;
13 | } else {
14 | return false;
15 | }
16 | };
17 |
18 | exports.emailCheckInDatabase = async function (email) {
19 | let user = await userModel.findOne({ email: email });
20 | user.exec((err, data) => {
21 | if (!data) {
22 | return false;
23 | } else {
24 | return true;
25 | }
26 | });
27 | };
28 |
29 | exports.phoneNumberCheckInDatabase = async function (phoneNumber) {
30 | let user = await userModel.findOne({ phoneNumber: phoneNumber });
31 | user.exec((err, data) => {
32 | if (data) {
33 | return true;
34 | } else {
35 | return false;
36 | }
37 | });
38 | };
39 |
--------------------------------------------------------------------------------
/server/config/keys.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | JWT_SECRET: "SecretKey",
3 | };
4 |
--------------------------------------------------------------------------------
/server/config/uploadFolderCreateScript.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 |
3 | const categoriesFolder = "./public/uploads/categories";
4 | const customizeFolder = "./public/uploads/customize";
5 | const productsFolder = "./public/uploads/products";
6 |
7 | const CreateAllFolder = () => {
8 | if (!fs.existsSync(categoriesFolder)) {
9 | fs.mkdirSync(categoriesFolder, {
10 | recursive: true,
11 | });
12 | }
13 |
14 | if (!fs.existsSync(customizeFolder)) {
15 | fs.mkdirSync(customizeFolder, {
16 | recursive: true,
17 | });
18 | }
19 |
20 | if (!fs.existsSync(productsFolder)) {
21 | fs.mkdirSync(productsFolder, {
22 | recursive: true,
23 | });
24 | }
25 | };
26 |
27 | module.exports = CreateAllFolder;
28 |
--------------------------------------------------------------------------------
/server/controller/auth.js:
--------------------------------------------------------------------------------
1 | const { toTitleCase, validateEmail } = require("../config/function");
2 | const bcrypt = require("bcryptjs");
3 | const userModel = require("../models/users");
4 | const jwt = require("jsonwebtoken");
5 | const { JWT_SECRET } = require("../config/keys");
6 |
7 | class Auth {
8 | async isAdmin(req, res) {
9 | let { loggedInUserId } = req.body;
10 | try {
11 | let loggedInUserRole = await userModel.findById(loggedInUserId);
12 | res.json({ role: loggedInUserRole.userRole });
13 | } catch {
14 | res.status(404);
15 | }
16 | }
17 |
18 | async allUser(req, res) {
19 | try {
20 | let allUser = await userModel.find({});
21 | res.json({ users: allUser });
22 | } catch {
23 | res.status(404);
24 | }
25 | }
26 |
27 | /* User Registration/Signup controller */
28 | async postSignup(req, res) {
29 | let { name, email, password, cPassword } = req.body;
30 | let error = {};
31 | if (!name || !email || !password || !cPassword) {
32 | error = {
33 | ...error,
34 | name: "Filed must not be empty",
35 | email: "Filed must not be empty",
36 | password: "Filed must not be empty",
37 | cPassword: "Filed must not be empty",
38 | };
39 | return res.json({ error });
40 | }
41 | if (name.length < 3 || name.length > 25) {
42 | error = { ...error, name: "Name must be 3-25 charecter" };
43 | return res.json({ error });
44 | } else {
45 | if (validateEmail(email)) {
46 | name = toTitleCase(name);
47 | if ((password.length > 255) | (password.length < 8)) {
48 | error = {
49 | ...error,
50 | password: "Password must be 8 charecter",
51 | name: "",
52 | email: "",
53 | };
54 | return res.json({ error });
55 | } else {
56 | // If Email & Number exists in Database then:
57 | try {
58 | password = bcrypt.hashSync(password, 10);
59 | const data = await userModel.findOne({ email: email });
60 | if (data) {
61 | error = {
62 | ...error,
63 | password: "",
64 | name: "",
65 | email: "Email already exists",
66 | };
67 | return res.json({ error });
68 | } else {
69 | let newUser = new userModel({
70 | name,
71 | email,
72 | password,
73 | // ========= Here role 1 for admin signup role 0 for customer signup =========
74 | userRole: 1, // Field Name change to userRole from role
75 | });
76 | newUser
77 | .save()
78 | .then((data) => {
79 | return res.json({
80 | success: "Account create successfully. Please login",
81 | });
82 | })
83 | .catch((err) => {
84 | console.log(err);
85 | });
86 | }
87 | } catch (err) {
88 | console.log(err);
89 | }
90 | }
91 | } else {
92 | error = {
93 | ...error,
94 | password: "",
95 | name: "",
96 | email: "Email is not valid",
97 | };
98 | return res.json({ error });
99 | }
100 | }
101 | }
102 |
103 | /* User Login/Signin controller */
104 | async postSignin(req, res) {
105 | let { email, password } = req.body;
106 | if (!email || !password) {
107 | return res.json({
108 | error: "Fields must not be empty",
109 | });
110 | }
111 | try {
112 | const data = await userModel.findOne({ email: email });
113 | if (!data) {
114 | return res.json({
115 | error: "Invalid email or password",
116 | });
117 | } else {
118 | const login = await bcrypt.compare(password, data.password);
119 | if (login) {
120 | const token = jwt.sign(
121 | { _id: data._id, role: data.userRole },
122 | JWT_SECRET
123 | );
124 | const encode = jwt.verify(token, JWT_SECRET);
125 | return res.json({
126 | token: token,
127 | user: encode,
128 | });
129 | } else {
130 | return res.json({
131 | error: "Invalid email or password",
132 | });
133 | }
134 | }
135 | } catch (err) {
136 | console.log(err);
137 | }
138 | }
139 | }
140 |
141 | const authController = new Auth();
142 | module.exports = authController;
143 |
--------------------------------------------------------------------------------
/server/controller/braintree.js:
--------------------------------------------------------------------------------
1 | var braintree = require("braintree");
2 | require("dotenv").config();
3 |
4 | var gateway = new braintree.BraintreeGateway({
5 | environment: braintree.Environment.Sandbox,
6 | merchantId: process.env.BRAINTREE_MERCHANT_ID,
7 | publicKey: process.env.BRAINTREE_PUBLIC_KEY,
8 | privateKey: process.env.BRAINTREE_PRIVATE_KEY,
9 | });
10 |
11 | class brainTree {
12 | ganerateToken(req, res) {
13 | gateway.clientToken.generate({}, (err, response) => {
14 | if (err) {
15 | return res.json(err);
16 | }
17 | return res.json(response);
18 | });
19 | }
20 |
21 | paymentProcess(req, res) {
22 | let { amountTotal, paymentMethod } = req.body;
23 | gateway.transaction.sale(
24 | {
25 | amount: amountTotal,
26 | paymentMethodNonce: paymentMethod,
27 | options: {
28 | submitForSettlement: true,
29 | },
30 | },
31 | (err, result) => {
32 | if (err) {
33 | console.error(err);
34 | return res.json(err);
35 | }
36 |
37 | if (result.success) {
38 | console.log("Transaction ID: " + result.transaction.id);
39 | return res.json(result);
40 | } else {
41 | console.error(result.message);
42 | }
43 | }
44 | );
45 | }
46 | }
47 |
48 | const brainTreeController = new brainTree();
49 | module.exports = brainTreeController;
50 |
--------------------------------------------------------------------------------
/server/controller/categories.js:
--------------------------------------------------------------------------------
1 | const { toTitleCase } = require("../config/function");
2 | const categoryModel = require("../models/categories");
3 | const fs = require("fs");
4 |
5 | class Category {
6 | async getAllCategory(req, res) {
7 | try {
8 | let Categories = await categoryModel.find({}).sort({ _id: -1 });
9 | if (Categories) {
10 | return res.json({ Categories });
11 | }
12 | } catch (err) {
13 | console.log(err);
14 | }
15 | }
16 |
17 | async postAddCategory(req, res) {
18 | let { cName, cDescription, cStatus } = req.body;
19 | let cImage = req.file.filename;
20 | const filePath = `../server/public/uploads/categories/${cImage}`;
21 |
22 | if (!cName || !cDescription || !cStatus || !cImage) {
23 | fs.unlink(filePath, (err) => {
24 | if (err) {
25 | console.log(err);
26 | }
27 | return res.json({ error: "All filled must be required" });
28 | });
29 | } else {
30 | cName = toTitleCase(cName);
31 | try {
32 | let checkCategoryExists = await categoryModel.findOne({ cName: cName });
33 | if (checkCategoryExists) {
34 | fs.unlink(filePath, (err) => {
35 | if (err) {
36 | console.log(err);
37 | }
38 | return res.json({ error: "Category already exists" });
39 | });
40 | } else {
41 | let newCategory = new categoryModel({
42 | cName,
43 | cDescription,
44 | cStatus,
45 | cImage,
46 | });
47 | await newCategory.save((err) => {
48 | if (!err) {
49 | return res.json({ success: "Category created successfully" });
50 | }
51 | });
52 | }
53 | } catch (err) {
54 | console.log(err);
55 | }
56 | }
57 | }
58 |
59 | async postEditCategory(req, res) {
60 | let { cId, cDescription, cStatus } = req.body;
61 | if (!cId || !cDescription || !cStatus) {
62 | return res.json({ error: "All filled must be required" });
63 | }
64 | try {
65 | let editCategory = categoryModel.findByIdAndUpdate(cId, {
66 | cDescription,
67 | cStatus,
68 | updatedAt: Date.now(),
69 | });
70 | let edit = await editCategory.exec();
71 | if (edit) {
72 | return res.json({ success: "Category edit successfully" });
73 | }
74 | } catch (err) {
75 | console.log(err);
76 | }
77 | }
78 |
79 | async getDeleteCategory(req, res) {
80 | let { cId } = req.body;
81 | if (!cId) {
82 | return res.json({ error: "All filled must be required" });
83 | } else {
84 | try {
85 | let deletedCategoryFile = await categoryModel.findById(cId);
86 | const filePath = `../server/public/uploads/categories/${deletedCategoryFile.cImage}`;
87 |
88 | let deleteCategory = await categoryModel.findByIdAndDelete(cId);
89 | if (deleteCategory) {
90 | // Delete Image from uploads -> categories folder
91 | fs.unlink(filePath, (err) => {
92 | if (err) {
93 | console.log(err);
94 | }
95 | return res.json({ success: "Category deleted successfully" });
96 | });
97 | }
98 | } catch (err) {
99 | console.log(err);
100 | }
101 | }
102 | }
103 | }
104 |
105 | const categoryController = new Category();
106 | module.exports = categoryController;
107 |
--------------------------------------------------------------------------------
/server/controller/customize.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const categoryModel = require("../models/categories");
3 | const productModel = require("../models/products");
4 | const orderModel = require("../models/orders");
5 | const userModel = require("../models/users");
6 | const customizeModel = require("../models/customize");
7 |
8 | class Customize {
9 | async getImages(req, res) {
10 | try {
11 | let Images = await customizeModel.find({});
12 | if (Images) {
13 | return res.json({ Images });
14 | }
15 | } catch (err) {
16 | console.log(err);
17 | }
18 | }
19 |
20 | async uploadSlideImage(req, res) {
21 | let image = req.file.filename;
22 | if (!image) {
23 | return res.json({ error: "All field required" });
24 | }
25 | try {
26 | let newCustomzie = new customizeModel({
27 | slideImage: image,
28 | });
29 | let save = await newCustomzie.save();
30 | if (save) {
31 | return res.json({ success: "Image upload successfully" });
32 | }
33 | } catch (err) {
34 | console.log(err);
35 | }
36 | }
37 |
38 | async deleteSlideImage(req, res) {
39 | let { id } = req.body;
40 | if (!id) {
41 | return res.json({ error: "All field required" });
42 | } else {
43 | try {
44 | let deletedSlideImage = await customizeModel.findById(id);
45 | const filePath = `../server/public/uploads/customize/${deletedSlideImage.slideImage}`;
46 |
47 | let deleteImage = await customizeModel.findByIdAndDelete(id);
48 | if (deleteImage) {
49 | // Delete Image from uploads -> customizes folder
50 | fs.unlink(filePath, (err) => {
51 | if (err) {
52 | console.log(err);
53 | }
54 | return res.json({ success: "Image deleted successfully" });
55 | });
56 | }
57 | } catch (err) {
58 | console.log(err);
59 | }
60 | }
61 | }
62 |
63 | async getAllData(req, res) {
64 | try {
65 | let Categories = await categoryModel.find({}).count();
66 | let Products = await productModel.find({}).count();
67 | let Orders = await orderModel.find({}).count();
68 | let Users = await userModel.find({}).count();
69 | if (Categories && Products && Orders) {
70 | return res.json({ Categories, Products, Orders, Users });
71 | }
72 | } catch (err) {
73 | console.log(err);
74 | }
75 | }
76 | }
77 |
78 | const customizeController = new Customize();
79 | module.exports = customizeController;
80 |
--------------------------------------------------------------------------------
/server/controller/orders.js:
--------------------------------------------------------------------------------
1 | const orderModel = require("../models/orders");
2 |
3 | class Order {
4 | async getAllOrders(req, res) {
5 | try {
6 | let Orders = await orderModel
7 | .find({})
8 | .populate("allProduct.id", "pName pImages pPrice")
9 | .populate("user", "name email")
10 | .sort({ _id: -1 });
11 | if (Orders) {
12 | return res.json({ Orders });
13 | }
14 | } catch (err) {
15 | console.log(err);
16 | }
17 | }
18 |
19 | async getOrderByUser(req, res) {
20 | let { uId } = req.body;
21 | if (!uId) {
22 | return res.json({ message: "All filled must be required" });
23 | } else {
24 | try {
25 | let Order = await orderModel
26 | .find({ user: uId })
27 | .populate("allProduct.id", "pName pImages pPrice")
28 | .populate("user", "name email")
29 | .sort({ _id: -1 });
30 | if (Order) {
31 | return res.json({ Order });
32 | }
33 | } catch (err) {
34 | console.log(err);
35 | }
36 | }
37 | }
38 |
39 | async postCreateOrder(req, res) {
40 | let { allProduct, user, amount, transactionId, address, phone } = req.body;
41 | if (
42 | !allProduct ||
43 | !user ||
44 | !amount ||
45 | !transactionId ||
46 | !address ||
47 | !phone
48 | ) {
49 | return res.json({ message: "All filled must be required" });
50 | } else {
51 | try {
52 | let newOrder = new orderModel({
53 | allProduct,
54 | user,
55 | amount,
56 | transactionId,
57 | address,
58 | phone,
59 | });
60 | let save = await newOrder.save();
61 | if (save) {
62 | return res.json({ success: "Order created successfully" });
63 | }
64 | } catch (err) {
65 | return res.json({ error: error });
66 | }
67 | }
68 | }
69 |
70 | async postUpdateOrder(req, res) {
71 | let { oId, status } = req.body;
72 | if (!oId || !status) {
73 | return res.json({ message: "All filled must be required" });
74 | } else {
75 | let currentOrder = orderModel.findByIdAndUpdate(oId, {
76 | status: status,
77 | updatedAt: Date.now(),
78 | });
79 | currentOrder.exec((err, result) => {
80 | if (err) console.log(err);
81 | return res.json({ success: "Order updated successfully" });
82 | });
83 | }
84 | }
85 |
86 | async postDeleteOrder(req, res) {
87 | let { oId } = req.body;
88 | if (!oId) {
89 | return res.json({ error: "All filled must be required" });
90 | } else {
91 | try {
92 | let deleteOrder = await orderModel.findByIdAndDelete(oId);
93 | if (deleteOrder) {
94 | return res.json({ success: "Order deleted successfully" });
95 | }
96 | } catch (error) {
97 | console.log(error);
98 | }
99 | }
100 | }
101 | }
102 |
103 | const ordersController = new Order();
104 | module.exports = ordersController;
105 |
--------------------------------------------------------------------------------
/server/controller/users.js:
--------------------------------------------------------------------------------
1 | const userModel = require("../models/users");
2 | const bcrypt = require("bcryptjs");
3 |
4 | class User {
5 | async getAllUser(req, res) {
6 | try {
7 | let Users = await userModel
8 | .find({})
9 | .populate("allProduct.id", "pName pImages pPrice")
10 | .populate("user", "name email")
11 | .sort({ _id: -1 });
12 | if (Users) {
13 | return res.json({ Users });
14 | }
15 | } catch (err) {
16 | console.log(err);
17 | }
18 | }
19 |
20 | async getSingleUser(req, res) {
21 | let { uId } = req.body;
22 | if (!uId) {
23 | return res.json({ error: "All filled must be required" });
24 | } else {
25 | try {
26 | let User = await userModel
27 | .findById(uId)
28 | .select("name email phoneNumber userImage updatedAt createdAt");
29 | if (User) {
30 | return res.json({ User });
31 | }
32 | } catch (err) {
33 | console.log(err);
34 | }
35 | }
36 | }
37 |
38 | async postAddUser(req, res) {
39 | let { allProduct, user, amount, transactionId, address, phone } = req.body;
40 | if (
41 | !allProduct ||
42 | !user ||
43 | !amount ||
44 | !transactionId ||
45 | !address ||
46 | !phone
47 | ) {
48 | return res.json({ message: "All filled must be required" });
49 | } else {
50 | try {
51 | let newUser = new userModel({
52 | allProduct,
53 | user,
54 | amount,
55 | transactionId,
56 | address,
57 | phone,
58 | });
59 | let save = await newUser.save();
60 | if (save) {
61 | return res.json({ success: "User created successfully" });
62 | }
63 | } catch (err) {
64 | return res.json({ error: error });
65 | }
66 | }
67 | }
68 |
69 | async postEditUser(req, res) {
70 | let { uId, name, phoneNumber } = req.body;
71 | if (!uId || !name || !phoneNumber) {
72 | return res.json({ message: "All filled must be required" });
73 | } else {
74 | let currentUser = userModel.findByIdAndUpdate(uId, {
75 | name: name,
76 | phoneNumber: phoneNumber,
77 | updatedAt: Date.now(),
78 | });
79 | currentUser.exec((err, result) => {
80 | if (err) console.log(err);
81 | return res.json({ success: "User updated successfully" });
82 | });
83 | }
84 | }
85 |
86 | async getDeleteUser(req, res) {
87 | let { oId, status } = req.body;
88 | if (!oId || !status) {
89 | return res.json({ message: "All filled must be required" });
90 | } else {
91 | let currentUser = userModel.findByIdAndUpdate(oId, {
92 | status: status,
93 | updatedAt: Date.now(),
94 | });
95 | currentUser.exec((err, result) => {
96 | if (err) console.log(err);
97 | return res.json({ success: "User updated successfully" });
98 | });
99 | }
100 | }
101 |
102 | async changePassword(req, res) {
103 | let { uId, oldPassword, newPassword } = req.body;
104 | if (!uId || !oldPassword || !newPassword) {
105 | return res.json({ message: "All filled must be required" });
106 | } else {
107 | const data = await userModel.findOne({ _id: uId });
108 | if (!data) {
109 | return res.json({
110 | error: "Invalid user",
111 | });
112 | } else {
113 | const oldPassCheck = await bcrypt.compare(oldPassword, data.password);
114 | if (oldPassCheck) {
115 | newPassword = bcrypt.hashSync(newPassword, 10);
116 | let passChange = userModel.findByIdAndUpdate(uId, {
117 | password: newPassword,
118 | });
119 | passChange.exec((err, result) => {
120 | if (err) console.log(err);
121 | return res.json({ success: "Password updated successfully" });
122 | });
123 | } else {
124 | return res.json({
125 | error: "Your old password is wrong!!",
126 | });
127 | }
128 | }
129 | }
130 | }
131 | }
132 |
133 | const ordersController = new User();
134 | module.exports = ordersController;
135 |
--------------------------------------------------------------------------------
/server/middleware/auth.js:
--------------------------------------------------------------------------------
1 | const jwt = require("jsonwebtoken");
2 | const { JWT_SECRET } = require("../config/keys");
3 | const userModel = require("../models/users");
4 |
5 | exports.loginCheck = (req, res, next) => {
6 | try {
7 | let token = req.headers.token;
8 | token = token.replace("Bearer ", "");
9 | decode = jwt.verify(token, JWT_SECRET);
10 | req.userDetails = decode;
11 | next();
12 | } catch (err) {
13 | res.json({
14 | error: "You must be logged in",
15 | });
16 | }
17 | };
18 |
19 | exports.isAuth = (req, res, next) => {
20 | let { loggedInUserId } = req.body;
21 | if (
22 | !loggedInUserId ||
23 | !req.userDetails._id ||
24 | loggedInUserId != req.userDetails._id
25 | ) {
26 | res.status(403).json({ error: "You are not authenticate" });
27 | }
28 | next();
29 | };
30 |
31 | exports.isAdmin = async (req, res, next) => {
32 | try {
33 | let reqUser = await userModel.findById(req.body.loggedInUserId);
34 | // If user role 0 that's mean not admin it's customer
35 | if (reqUser.userRole === 0) {
36 | res.status(403).json({ error: "Access denied" });
37 | }
38 | next();
39 | } catch {
40 | res.status(404);
41 | }
42 | };
43 |
--------------------------------------------------------------------------------
/server/models/categories.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const categorySchema = new mongoose.Schema(
4 | {
5 | cName: {
6 | type: String,
7 | required: true,
8 | },
9 | cDescription: {
10 | type: String,
11 | required: true,
12 | },
13 | cImage: {
14 | type: String,
15 | },
16 | cStatus: {
17 | type: String,
18 | required: true,
19 | },
20 | },
21 | { timestamps: true }
22 | );
23 |
24 | const categoryModel = mongoose.model("categories", categorySchema);
25 | module.exports = categoryModel;
26 |
--------------------------------------------------------------------------------
/server/models/customize.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const customizeSchema = new mongoose.Schema(
4 | {
5 | slideImage: {
6 | type: String,
7 | },
8 | firstShow: {
9 | type: Number,
10 | default: 0,
11 | },
12 | },
13 | { timestamps: true }
14 | );
15 |
16 | const customizeModel = mongoose.model("customizes", customizeSchema);
17 | module.exports = customizeModel;
18 |
--------------------------------------------------------------------------------
/server/models/orders.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const { ObjectId } = mongoose.Schema.Types;
3 |
4 | const orderSchema = new mongoose.Schema(
5 | {
6 | allProduct: [
7 | {
8 | id: { type: ObjectId, ref: "products" },
9 | quantitiy: Number,
10 | },
11 | ],
12 | user: {
13 | type: ObjectId,
14 | ref: "users",
15 | required: true,
16 | },
17 | amount: {
18 | type: Number,
19 | required: true,
20 | },
21 | transactionId: {
22 | type: String,
23 | required: true,
24 | },
25 | address: {
26 | type: String,
27 | required: true,
28 | },
29 | phone: {
30 | type: Number,
31 | required: true,
32 | },
33 | status: {
34 | type: String,
35 | default: "Not processed",
36 | enum: [
37 | "Not processed",
38 | "Processing",
39 | "Shipped",
40 | "Delivered",
41 | "Cancelled",
42 | ],
43 | },
44 | },
45 | { timestamps: true }
46 | );
47 |
48 | const orderModel = mongoose.model("orders", orderSchema);
49 | module.exports = orderModel;
50 |
--------------------------------------------------------------------------------
/server/models/products.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 | const { ObjectId } = mongoose.Schema.Types;
3 |
4 | const productSchema = new mongoose.Schema(
5 | {
6 | pName: {
7 | type: String,
8 | required: true,
9 | },
10 | pDescription: {
11 | type: String,
12 | required: true,
13 | },
14 | pPrice: {
15 | type: Number,
16 | required: true,
17 | },
18 | pSold: {
19 | type: Number,
20 | default: 0,
21 | },
22 | pQuantity: {
23 | type: Number,
24 | default: 0,
25 | },
26 | pCategory: {
27 | type: ObjectId,
28 | ref: "categories",
29 | },
30 | pImages: {
31 | type: Array,
32 | required: true,
33 | },
34 | pOffer: {
35 | type: String,
36 | default: null,
37 | },
38 | pRatingsReviews: [
39 | {
40 | review: String,
41 | user: { type: ObjectId, ref: "users" },
42 | rating: String,
43 | createdAt: {
44 | type: Date,
45 | default: Date.now(),
46 | },
47 | },
48 | ],
49 | pStatus: {
50 | type: String,
51 | required: true,
52 | },
53 | },
54 | { timestamps: true }
55 | );
56 |
57 | const productModel = mongoose.model("products", productSchema);
58 | module.exports = productModel;
59 |
--------------------------------------------------------------------------------
/server/models/users.js:
--------------------------------------------------------------------------------
1 | const mongoose = require("mongoose");
2 |
3 | const userSchema = new mongoose.Schema(
4 | {
5 | name: {
6 | type: String,
7 | required: true,
8 | maxlength: 32,
9 | },
10 | email: {
11 | type: String,
12 | required: true,
13 | trim: true,
14 | index: { unique: true },
15 | match: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
16 | },
17 | password: {
18 | type: String,
19 | required: true,
20 | },
21 | userRole: {
22 | type: Number,
23 | required: true,
24 | },
25 | phoneNumber: {
26 | type: Number,
27 | },
28 | userImage: {
29 | type: String,
30 | default: "user.png",
31 | },
32 | verified: {
33 | type: String,
34 | default: false,
35 | },
36 | secretKey: {
37 | type: String,
38 | default: null,
39 | },
40 | history: {
41 | type: Array,
42 | default: [],
43 | },
44 | },
45 | { timestamps: true }
46 | );
47 |
48 | const userModel = mongoose.model("users", userSchema);
49 | module.exports = userModel;
50 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Hayroo",
3 | "version": "1.0.0",
4 | "description": "A E-commerce website.",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start:dev": "nodemon app.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/hasan-py/MERN_Stack_Project_Hayroo_Ecommerce"
13 | },
14 | "author": "Hasan | https://hasan-py.github.io",
15 | "license": "ISC",
16 | "bugs": {
17 | "url": "https://github.com/hasan-py/MERN_Stack_Project_Hayroo_Ecommerce/issues"
18 | },
19 | "homepage": "https://github.com/hasan-py/MERN_Stack_Project_Hayroo_Ecommerce#readme",
20 | "dependencies": {
21 | "bcryptjs": "^2.4.3",
22 | "braintree": "^3.0.0",
23 | "cookie-parser": "^1.4.5",
24 | "cors": "^2.8.5",
25 | "dotenv": "^8.2.0",
26 | "express": "^4.17.1",
27 | "i": "^0.3.6",
28 | "jsonwebtoken": "^8.5.1",
29 | "mongoose": "^5.9.27",
30 | "morgan": "^1.10.0",
31 | "multer": "^1.4.2",
32 | "nodemon": "^2.0.4",
33 | "npm": "^6.14.8"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/server/routes/auth.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 | const authController = require("../controller/auth");
4 | const { loginCheck, isAuth, isAdmin } = require("../middleware/auth");
5 |
6 | router.post("/isadmin", authController.isAdmin);
7 | router.post("/signup", authController.postSignup);
8 | router.post("/signin", authController.postSignin);
9 | router.post("/user", loginCheck, isAuth, isAdmin, authController.allUser);
10 |
11 | module.exports = router;
12 |
--------------------------------------------------------------------------------
/server/routes/braintree.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 | const brainTreeController = require("../controller/braintree");
4 |
5 | router.post("/braintree/get-token", brainTreeController.ganerateToken);
6 | router.post("/braintree/payment", brainTreeController.paymentProcess);
7 |
8 | module.exports = router;
9 |
--------------------------------------------------------------------------------
/server/routes/categories.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 | const categoryController = require("../controller/categories");
4 | const multer = require("multer");
5 | const { loginCheck } = require("../middleware/auth");
6 |
7 | // Image Upload setting
8 | var storage = multer.diskStorage({
9 | destination: function (req, file, cb) {
10 | cb(null, "./public/uploads/categories");
11 | },
12 | filename: function (req, file, cb) {
13 | cb(null, Date.now() + "_" + file.originalname);
14 | },
15 | });
16 |
17 | const upload = multer({ storage: storage });
18 |
19 | router.get("/all-category", categoryController.getAllCategory);
20 | router.post(
21 | "/add-category",
22 | loginCheck,
23 | upload.single("cImage"),
24 | categoryController.postAddCategory
25 | );
26 | router.post("/edit-category", loginCheck, categoryController.postEditCategory);
27 | router.post(
28 | "/delete-category",
29 | loginCheck,
30 | categoryController.getDeleteCategory
31 | );
32 |
33 | module.exports = router;
34 |
--------------------------------------------------------------------------------
/server/routes/customize.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 | const customizeController = require("../controller/customize");
4 | const multer = require("multer");
5 |
6 | var storage = multer.diskStorage({
7 | destination: function (req, file, cb) {
8 | cb(null, "public/uploads/customize");
9 | },
10 | filename: function (req, file, cb) {
11 | cb(null, Date.now() + "_" + file.originalname);
12 | },
13 | });
14 |
15 | const upload = multer({ storage: storage });
16 |
17 | router.get("/get-slide-image", customizeController.getImages);
18 | router.post("/delete-slide-image", customizeController.deleteSlideImage);
19 | router.post(
20 | "/upload-slide-image",
21 | upload.single("image"),
22 | customizeController.uploadSlideImage
23 | );
24 | router.post("/dashboard-data", customizeController.getAllData);
25 |
26 | module.exports = router;
27 |
--------------------------------------------------------------------------------
/server/routes/orders.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 | const ordersController = require("../controller/orders");
4 |
5 | router.get("/get-all-orders", ordersController.getAllOrders);
6 | router.post("/order-by-user", ordersController.getOrderByUser);
7 |
8 | router.post("/create-order", ordersController.postCreateOrder);
9 | router.post("/update-order", ordersController.postUpdateOrder);
10 | router.post("/delete-order", ordersController.postDeleteOrder);
11 |
12 | module.exports = router;
13 |
--------------------------------------------------------------------------------
/server/routes/products.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 | const productController = require("../controller/products");
4 | const multer = require("multer");
5 |
6 | var storage = multer.diskStorage({
7 | destination: function (req, file, cb) {
8 | cb(null, "public/uploads/products");
9 | },
10 | filename: function (req, file, cb) {
11 | cb(null, Date.now() + "_" + file.originalname);
12 | },
13 | });
14 |
15 | const upload = multer({ storage: storage });
16 |
17 | router.get("/all-product", productController.getAllProduct);
18 | router.post("/product-by-category", productController.getProductByCategory);
19 | router.post("/product-by-price", productController.getProductByPrice);
20 | router.post("/wish-product", productController.getWishProduct);
21 | router.post("/cart-product", productController.getCartProduct);
22 |
23 | router.post("/add-product", upload.any(), productController.postAddProduct);
24 | router.post("/edit-product", upload.any(), productController.postEditProduct);
25 | router.post("/delete-product", productController.getDeleteProduct);
26 | router.post("/single-product", productController.getSingleProduct);
27 |
28 | router.post("/add-review", productController.postAddReview);
29 | router.post("/delete-review", productController.deleteReview);
30 |
31 | module.exports = router;
32 |
--------------------------------------------------------------------------------
/server/routes/users.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const router = express.Router();
3 | const usersController = require("../controller/users");
4 |
5 | router.get("/all-user", usersController.getAllUser);
6 | router.post("/signle-user", usersController.getSingleUser);
7 |
8 | router.post("/add-user", usersController.postAddUser);
9 | router.post("/edit-user", usersController.postEditUser);
10 | router.post("/delete-user", usersController.getDeleteUser);
11 |
12 | router.post("/change-password", usersController.changePassword);
13 |
14 | module.exports = router;
15 |
--------------------------------------------------------------------------------