diff --git a/package-lock.json b/package-lock.json
index c2c41d3..3bb9685 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
+ "axios": "^0.25.0",
"jalali-moment": "^3.3.10",
"lord-icon-element": "^3.3.3",
"lottie-web": "^5.8.1",
@@ -22,7 +23,10 @@
"react-router-dom": "^6.2.1",
"react-scripts": "5.0.0",
"react-slideshow-image": "^3.7.0",
+ "react-toastify": "^8.1.1",
"redux": "^4.1.2",
+ "redux-devtools-extension": "^2.13.9",
+ "redux-thunk": "^2.4.1",
"sass": "^1.45.1",
"web-vitals": "^2.1.2"
},
@@ -4330,6 +4334,14 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
+ "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
+ "dependencies": {
+ "follow-redirects": "^1.14.7"
+ }
+ },
"node_modules/axobject-query": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
@@ -5129,6 +5141,14 @@
"node": ">=0.8"
}
},
+ "node_modules/clsx": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+ "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -7579,9 +7599,9 @@
"integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw=="
},
"node_modules/follow-redirects": {
- "version": "1.14.6",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
- "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
+ "version": "1.14.8",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
+ "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==",
"funding": [
{
"type": "individual",
@@ -13951,6 +13971,18 @@
"react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0"
}
},
+ "node_modules/react-toastify": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.1.1.tgz",
+ "integrity": "sha512-KQa+j+JHjy69xpPYheaWUq3SjVw1sV2D7/QsXW6Tpo2x9HhUCvASezAMYDmLhfAYoSnaXUVnTNFAgZQA75SExQ==",
+ "dependencies": {
+ "clsx": "^1.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -14006,6 +14038,23 @@
"@babel/runtime": "^7.9.2"
}
},
+ "node_modules/redux-devtools-extension": {
+ "version": "2.13.9",
+ "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz",
+ "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==",
+ "deprecated": "Package moved to @redux-devtools/extension.",
+ "peerDependencies": {
+ "redux": "^3.1.0 || ^4.0.0"
+ }
+ },
+ "node_modules/redux-thunk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
+ "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
+ "peerDependencies": {
+ "redux": "^4"
+ }
+ },
"node_modules/regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
@@ -19863,6 +19912,14 @@
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz",
"integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA=="
},
+ "axios": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz",
+ "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==",
+ "requires": {
+ "follow-redirects": "^1.14.7"
+ }
+ },
"axobject-query": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
@@ -20463,6 +20520,11 @@
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
},
+ "clsx": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
+ "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
+ },
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@@ -22269,9 +22331,9 @@
"integrity": "sha512-8/sOawo8tJ4QOBX8YlQBMxL8+RLZfxMQOif9o0KUKTNTjMYElWPE0r/m5VNFxTRd0NSw8qSy8dajrwX4RYI1Hw=="
},
"follow-redirects": {
- "version": "1.14.6",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
- "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A=="
+ "version": "1.14.8",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz",
+ "integrity": "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA=="
},
"fork-ts-checker-webpack-plugin": {
"version": "6.5.0",
@@ -26694,6 +26756,14 @@
"resize-observer-polyfill": "^1.5.1"
}
},
+ "react-toastify": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.1.1.tgz",
+ "integrity": "sha512-KQa+j+JHjy69xpPYheaWUq3SjVw1sV2D7/QsXW6Tpo2x9HhUCvASezAMYDmLhfAYoSnaXUVnTNFAgZQA75SExQ==",
+ "requires": {
+ "clsx": "^1.1.1"
+ }
+ },
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
@@ -26737,6 +26807,18 @@
"@babel/runtime": "^7.9.2"
}
},
+ "redux-devtools-extension": {
+ "version": "2.13.9",
+ "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz",
+ "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==",
+ "requires": {}
+ },
+ "redux-thunk": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz",
+ "integrity": "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==",
+ "requires": {}
+ },
"regenerate": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz",
diff --git a/package.json b/package.json
index 3916525..cbd3361 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
+ "axios": "^0.25.0",
"jalali-moment": "^3.3.10",
"lord-icon-element": "^3.3.3",
"lottie-web": "^5.8.1",
@@ -17,7 +18,10 @@
"react-router-dom": "^6.2.1",
"react-scripts": "5.0.0",
"react-slideshow-image": "^3.7.0",
+ "react-toastify": "^8.1.1",
"redux": "^4.1.2",
+ "redux-devtools-extension": "^2.13.9",
+ "redux-thunk": "^2.4.1",
"sass": "^1.45.1",
"web-vitals": "^2.1.2"
},
diff --git a/src/App.js b/src/App.js
index d55df78..8770ee7 100644
--- a/src/App.js
+++ b/src/App.js
@@ -22,6 +22,9 @@ import LoginSignUp from './view/LoginSignUp';
import Register from './view/register'
import Services from './view/services';
+import {Provider, provider} from 'react-redux';
+import store from './redux/store';
+
let data = [
{
@@ -641,6 +644,8 @@ function App() {
};
return (
+
+
{LOGIN && }
@@ -677,6 +682,7 @@ function App() {
+
);
}
diff --git a/src/components/Cart/index.js b/src/components/Cart/index.js
new file mode 100644
index 0000000..82ea0dd
--- /dev/null
+++ b/src/components/Cart/index.js
@@ -0,0 +1,41 @@
+import React from 'react';
+import { connect } from 'react-redux';
+import { createStructuredSelector } from "reselect";
+import { withRouter } from 'react-router-dom';
+
+
+
+import { selectCartItems } from "../../redux/cart/cart.selectors";
+import { toggleCartHidden } from '../../redux/cart/cart.actions';
+
+
+import './cart-dropdown.styles.scss';
+
+const CartDropdown = ({ cartItems, history, dispatch }) => (
+
-
{title}
-
{description}
- {teacher} {group} {price}
+import React from 'react';
+import { connect } from 'react-redux';
+
+import { addItem }from '../../../redux/cart/cart.actions.js';
+
+
+
+const Class = ({item, addItem}) => {
+ const {name, price, imageUrl} = item;
+ return (
+
+
+
+
+ {name}
+ {price}
- )
-};
-export default Class
\ No newline at end of file
+
+
+ );
+}
+
+const mapDispatchToProps = dispatch => ({
+ addItem: item => dispatch(addItem(item))
+})
+
+export default connect(null, mapDispatchToProps) (Class);
\ No newline at end of file
diff --git a/src/constants/colors.scss b/src/constants/colors.scss
new file mode 100644
index 0000000..a1637fd
--- /dev/null
+++ b/src/constants/colors.scss
@@ -0,0 +1,36 @@
+// gray
+$gray : #aaa;
+$gray1 : #383838;
+$gray2 : #6c6c6c;
+$gray3 : rgb(143, 142, 142);
+$gray4 : #666666;
+$gray5 : #555;
+$gray6 : #ddd;
+$gray7 : #797979;
+$gray8 : #272727;
+$gray9 : #b3b3b3;
+$gray10 : #cccccc;
+$gray11 : #969696;
+$gray12 : #e9e9e9;
+$gray13 : #f6f6f6;
+// green
+$green : #4dd35b;
+$green1 : #81C342;
+$green2 : #6cbe44;
+$green3 : #00cc69;
+$green4 : #84de56;
+$green5 : #9cdf52;
+$green6 : #08793d;
+// blue
+$blue : #a2b4cb;
+$blue1 : #505a82;
+$blue2 : #00FFF8;
+// red
+$red : red;
+$red1 : rgb(255, 115, 115);
+
+//yellow
+$yellow : #FFD70F;
+
+//orange
+$orange : orange;
\ No newline at end of file
diff --git a/src/constants/defaultValues.js b/src/constants/defaultValues.js
new file mode 100644
index 0000000..2591544
--- /dev/null
+++ b/src/constants/defaultValues.js
@@ -0,0 +1,9 @@
+export const ApiConfig = {
+ apiKey: 'AIzaSyBBksq-Asxq2M4Ot-75X19IyrEYJqNBPcg',
+ authDomain: 'dnvn.ir',
+ baseUrl: 'https://dnvn.ir/api/v1',
+ //baseUrl: 'http://localhost:3030/api/v1',
+ loginURL: 'user/login',
+ otploginURL: 'user/otp/login',
+ logoutURL: 'user/logout',
+};
\ No newline at end of file
diff --git a/src/constants/size.scss b/src/constants/size.scss
new file mode 100644
index 0000000..2ca18c5
--- /dev/null
+++ b/src/constants/size.scss
@@ -0,0 +1,8 @@
+$xs : 320px;
+$sm : 640px;
+$md : 768px;
+$lg : 1024px;
+$xl : 1280px;
+$xxl : 1536px;
+
+$maxWidth : calc(100% - 120px);
\ No newline at end of file
diff --git a/src/redux/actions/blog.js b/src/redux/actions/blog.js
new file mode 100644
index 0000000..d873065
--- /dev/null
+++ b/src/redux/actions/blog.js
@@ -0,0 +1,27 @@
+import proxy from "../proxy";
+const blog = {
+ list:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("public/blogList", data, { dispatch }),
+ // update:
+ // (data = {}, data2 = {}) =>
+ // async (dispatch : Dispatch) => {
+ // await proxy.put("blog/update", data);
+ // await proxy.get("blog/list", data2, { dispatch });
+ // },
+ // add:
+ // (data = {}, data2 = {}) =>
+ // async (dispatch : Dispatch) => {
+ // await proxy.post("blog/add", data);
+ // await proxy.get("blog/list", data2, { dispatch });
+ // },
+ // del:
+ // (data = {}, data2 = {}) =>
+ // async (dispatch : Dispatch) => {
+ // await proxy.delete("blog/delete", data);
+ // await proxy.get("blog/list", data2, { dispatch });
+ // },
+};
+
+export default blog;
diff --git a/src/redux/actions/book.js b/src/redux/actions/book.js
new file mode 100644
index 0000000..f74ec70
--- /dev/null
+++ b/src/redux/actions/book.js
@@ -0,0 +1,44 @@
+import proxy from "../proxy";
+const book = {
+ list:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("book/list", data, { dispatch }),
+ info:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("book/info", data, { dispatch }),
+ setLessonActive: (data) => async (dispatch) =>
+ await dispatch({ type: "book/lesson/active", data: data }),
+ gradeFilter: (data) => async (dispatch) =>
+ await dispatch({ type: "book/gradeFilter", data: data }),
+ sortFilter: (data) => async (dispatch) =>
+ await dispatch({ type: "book/sortFilter", data: data }),
+
+ // update:
+ // (data = {}, data2 = {}) =>
+ // async (dispatch : Dispatch) => {
+ // await proxy.put("book/update", data);
+ // await proxy.get("book/list", data2, { dispatch });
+ // },
+ // add:
+ // (data = {}, data2 = {}) =>
+ // async (dispatch) => {
+ // await proxy.post("book/add", data);
+ // await proxy.get("book/list", data2, { dispatch });
+ // },
+ // del:
+ // (data = {}, data2 = {}) =>
+ // async (dispatch) => {
+ // await proxy.delete("book/delete", data);
+ // await proxy.get("book/list", data2, { dispatch });
+ // },
+ // searchBook: (data) => async (dispatch) => {
+ // dispatch({ type: "book/search", data: data });
+ // },
+ // filterBook: (data) => async (dispatch) => {
+ // dispatch({ type: "book/filter", data: data });
+ // },
+};
+
+export default book;
diff --git a/src/redux/actions/content.js b/src/redux/actions/content.js
new file mode 100644
index 0000000..e09c8c8
--- /dev/null
+++ b/src/redux/actions/content.js
@@ -0,0 +1,14 @@
+import proxy from "../proxy";
+import { Dispatch } from "redux";
+const content = {
+ listVOD:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("content/listVOD", data, { dispatch }),
+ infoVOD:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("content/infoVOD", data, { dispatch }),
+};
+
+export default content;
diff --git a/src/redux/actions/faq.js b/src/redux/actions/faq.js
new file mode 100644
index 0000000..aaf031b
--- /dev/null
+++ b/src/redux/actions/faq.js
@@ -0,0 +1,20 @@
+import proxy from "../proxy";
+import { Dispatch } from "redux";
+const faq = {
+ list:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("faq/list", data, { dispatch }),
+ add:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.post("faq/add", data, { dispatch }),
+ selectFaq:
+ (data = {}) =>
+ async (dispatch) =>
+ await dispatch({ type: "faq/selectFaq", data: data }),
+ searchFaq: (data) => async (dispatch) =>
+ await dispatch({ type: "faq/search", data: data }),
+};
+
+export default faq;
diff --git a/src/redux/actions/file.js b/src/redux/actions/file.js
new file mode 100644
index 0000000..02090a1
--- /dev/null
+++ b/src/redux/actions/file.js
@@ -0,0 +1,22 @@
+import proxy from "../proxy";
+const file = {
+ upload:
+ (data = {}) =>
+ async (dispatch) => {
+ var formData = new FormData();
+ for (let key in data) formData.append(key, data[key]);
+ return await proxy.post(
+ "file/upload",
+ formData,
+ {
+ dispatch,
+ headers: {
+ "Content-Type": "multipart/form-data",
+ },
+ },
+ data
+ );
+ },
+};
+
+export default file;
diff --git a/src/redux/actions/index.js b/src/redux/actions/index.js
new file mode 100644
index 0000000..cc0da2a
--- /dev/null
+++ b/src/redux/actions/index.js
@@ -0,0 +1,18 @@
+// @create-index
+export { default as publicApi } from "./public";
+export { default as user } from "./user";
+export { default as book } from "./book";
+export { default as blog } from "./blog";
+export { default as product } from "./product";
+export { default as userFactor } from "./userFactor";
+export { default as userProduct } from "./userProduct";
+export { default as content } from "./content";
+export { default as faq } from "./faq";
+export { default as file } from "./file";
+export {default as scrollAction} from "./scroll";
+export {default as qr} from "./qr";
+
+
+
+
+
diff --git a/src/redux/actions/product.js b/src/redux/actions/product.js
new file mode 100644
index 0000000..0dabd1e
--- /dev/null
+++ b/src/redux/actions/product.js
@@ -0,0 +1,30 @@
+import proxy from "../proxy";
+const product = {
+ getSimilar:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("product/similar", data, { dispatch }),
+ listVOD:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("product/listVOD", data, { dispatch }),
+ addVOD:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.post("product/addVOD", data);
+ await proxy.get("product/listVOD", data2, { dispatch });
+ },
+ deleteVOD:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.delete("product/deleteVOD", data);
+ await proxy.get("product/listVOD", data2, { dispatch });
+ },
+ selectVOD:
+ (data = {}) =>
+ async (dispatch) => {
+ await dispatch({ type: "product/selectVOD", data });
+ },
+};
+
+export default product;
diff --git a/src/redux/actions/public.js b/src/redux/actions/public.js
new file mode 100644
index 0000000..3755441
--- /dev/null
+++ b/src/redux/actions/public.js
@@ -0,0 +1,56 @@
+import proxy from "../proxy";
+const publicApi = {
+ setDesktopContentTransform: (data) => async (dispatch) =>
+ await dispatch({ type: "public/setDesktopContentTransform", data }),
+ setHeaderOptions: (data) => async (dispatch) =>
+ await dispatch({ type: "public/setHeaderOptions", data }),
+ setIsDark: (data) => async (dispatch) =>
+ await dispatch({ type: "public/setIsDark", data }),
+ getHomeData: (data) => async (dispatch) =>
+ await proxy.get("public/homePage", data, { dispatch }),
+
+ setFaqActive: (data) => async (dispatch) =>
+ await dispatch({ type: "public/faq/activate", data }),
+ bookInfo:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("public/vod/bookCourse", data, { dispatch }),
+ bookSection:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("public/vod/bookSection", data, { dispatch }),
+ getContactData:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("public/contact", data, { dispatch }),
+
+ getBlogList: (data) => async (dispatch) =>
+ await proxy.get("public/blogList", data, { dispatch }),
+
+ getBlogInfo: (data) => async (dispatch) =>
+ await proxy.get("public/blogInfo", data, { dispatch }),
+ setSubscribe: (data) => async (dispatch) =>
+ await dispatch({ type: "public/setSubscribe", data }),
+ getProvince:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("public/province", data, { dispatch }),
+ getCity:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("public/city", data, { dispatch }),
+ selectMenu: (data) => async (dispatch) =>
+ await dispatch({ type: "public/selectMenu", data }),
+ getHeader:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("public/VOD/homePage", data, { dispatch }),
+ activeCategory: (data) => async (dispatch) =>
+ await dispatch({ type: "active/category", data }),
+ setVideoActive: (data) => async (dispatch) =>
+ await dispatch({ type: "active/video", data }),
+ getArList: (data, history) => async (dispatch) =>
+ await proxy.get("public/ar", data, { history, dispatch }),
+};
+
+export default publicApi;
diff --git a/src/redux/actions/qr.js b/src/redux/actions/qr.js
new file mode 100644
index 0000000..06dabbb
--- /dev/null
+++ b/src/redux/actions/qr.js
@@ -0,0 +1,32 @@
+import proxy from "../proxy";
+const qr = {
+ list:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("qr/list", data, { dispatch }),
+ info:
+ (data = {}) =>
+ async (dispatch) => {
+ await proxy.get("qr/info", data, { dispatch });
+ },
+ add:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.post("qr/add", data);
+ await proxy.get("qr/list", data2, { dispatch });
+ },
+ del:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.delete("ar/delete", data);
+ await proxy.get("ar/list", data2, { dispatch });
+ },
+ update:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.put("ar/update", data);
+ await proxy.get("ar/list", data2, { dispatch });
+ },
+};
+
+export default qr;
\ No newline at end of file
diff --git a/src/redux/actions/scroll.js b/src/redux/actions/scroll.js
new file mode 100644
index 0000000..ef8e431
--- /dev/null
+++ b/src/redux/actions/scroll.js
@@ -0,0 +1,74 @@
+const scrollAction = {
+ floatMouseToggle:
+ (data = null) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page1/floatMouse", data });
+ },
+ handlePage1TopPosition:
+ (data = null) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page1/top", data });
+ },
+ handlePage2TopPosition:
+ (data = null) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page2/top", data });
+ },
+ handlePage2RotateX:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page2/rotateX", data });
+ },
+ handlePage2BoxPosition:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page2/boxPosition", data });
+ },
+ handlePage2MobilePosition:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page2/mobilePosition", data });
+ },
+ handlePage2VideoPosition:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page2/videoPosition", data });
+ },
+ handlePage2VideoTopPosition:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page2/videoTopPosition", data });
+ },
+ handlePage2MobileUnit:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page2/mobileUnit", data });
+ },
+ handleVideoTubeTopPosition:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/videoTube/top", data });
+ },
+ handleTestTranslateY:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/test/translateY", data });
+ },
+ handlePage3TopPosition:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page3/top", data });
+ },
+ handlePage3TestTranslateY:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page3/testTranslateY", data });
+ },
+ handlePage4TopPosition:
+ (data = window.scrollY) =>
+ async (dispatch) => {
+ await dispatch({ type: "scroll/page4/top", data });
+ },
+};
+
+export default scrollAction;
diff --git a/src/redux/actions/user.js b/src/redux/actions/user.js
new file mode 100644
index 0000000..31fe949
--- /dev/null
+++ b/src/redux/actions/user.js
@@ -0,0 +1,63 @@
+import proxy from "../proxy";
+
+const user = {
+ otp: (data) => async (dispatch) =>
+ await proxy.post("public/sendOTP", data, { dispatch }),
+
+ otp_login: (data) => async (dispatch) =>
+ await proxy.login("user/otp/login", data, { dispatch }),
+
+ login: (data) => async (dispatch) =>
+ await proxy.login("user/login", data, { dispatch }),
+
+ // register: (data : undefined) => async (dispatch : Dispatch) =>
+ // await proxy.login("user/register", data, { dispatch }),
+
+ // switchRole: (data : String) => async (dispatch : Dispatch) =>
+ // await proxy.login("user/switchRole", data, { dispatch }),
+
+ logout: (data) => async (dispatch) =>
+ await proxy.logout("user/logout", data, { dispatch }),
+
+ getUserRole: (data) => async (dispatch) =>
+ await proxy.get("user/getUserRole", data, { dispatch }),
+
+ // list:
+ // (data = {}) =>
+ // async (dispatch : Dispatch) =>
+ // await proxy.get("user/list", data, { dispatch }),
+ // domains:
+ // (data = {}) =>
+ // async (dispatch : Dispatch) =>
+ // await proxy.get("user/domains", data, { dispatch }),
+ // getmothers:
+ // (data = {}) =>
+ // async (dispatch : Dispatch) =>
+ // await proxy.get("mothers/list", data, {
+ // dispatch,
+ // }),
+ getProfile:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("user/getProfile", data, { dispatch }),
+ setProfile:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.put("user/setProfile", data, { dispatch });
+ await proxy.get("user/getProfile", data2, { dispatch });
+ },
+ add:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.post("user/add", data);
+ await proxy.get("user/list", data2, { dispatch });
+ },
+ del:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.delete("user/delete", data);
+ await proxy.get("user/list", data2, { dispatch });
+ },
+};
+
+export default user;
diff --git a/src/redux/actions/userFactor.js b/src/redux/actions/userFactor.js
new file mode 100644
index 0000000..a46962c
--- /dev/null
+++ b/src/redux/actions/userFactor.js
@@ -0,0 +1,55 @@
+import proxy from "../proxy";
+const userFactor = {
+ //dnvn
+ list:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("userFactor/list", data, { dispatch }),
+ payment:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.post("userFactor/payment", data, { dispatch });
+ },
+ add:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.post("userFactor/add", data, { dispatch });
+ await proxy.get("userFactor/list", data2, { dispatch });
+ },
+ del:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.delete("userFactor/delete", data);
+ await proxy.get("userFactor/list", data2, { dispatch });
+ },
+
+ //vod
+ payment:
+ (data1 = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.post("userProduct/addVOD", data1, { dispatch });
+ await proxy.post("userFactor/paymentVOD", data2, { dispatch });
+ },
+ verify:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.post("userFactor/verifyVOD", data, { dispatch }),
+ info:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("userFactor/info", data, { dispatch }),
+ update:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.put("userFactor/update", data, { dispatch });
+ await proxy.get("userFactor/info", data2, { dispatch });
+ },
+ deleteUserOffCode:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.delete("userFactor/deleteUserOffCode", data, { dispatch });
+ await proxy.get("userFactor/info", data2, { dispatch });
+ },
+};
+
+export default userFactor;
diff --git a/src/redux/actions/userProduct.js b/src/redux/actions/userProduct.js
new file mode 100644
index 0000000..2436d7f
--- /dev/null
+++ b/src/redux/actions/userProduct.js
@@ -0,0 +1,33 @@
+import proxy from "../proxy";
+const userProduct = {
+ list:
+ (data = {}) =>
+ async (dispatch) => {
+ await proxy.get("userProduct/list", data, { dispatch });
+ },
+ info:
+ (data = {}) =>
+ async (dispatch) =>
+ await proxy.get("userProduct/info", data, { dispatch }),
+ update:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.put("userProduct/update", data);
+ await proxy.get("userProduct/list", data2, { dispatch });
+ },
+ add:
+ (data = {}, data2 = {}, data3 = {}) =>
+ async (dispatch) => {
+ await proxy.post("userProduct/add", data, { dispatch });
+ await proxy.get("userProduct/list", data2, { dispatch });
+ await proxy.get("userFactor/info", data3, { dispatch });
+ },
+ del:
+ (data = {}, data2 = {}) =>
+ async (dispatch) => {
+ await proxy.delete("userProduct/delete", data);
+ await proxy.get("userProduct/list", data2, { dispatch });
+ },
+};
+
+export default userProduct;
diff --git a/src/redux/cart/card.types.js b/src/redux/cart/card.types.js
new file mode 100644
index 0000000..c52159e
--- /dev/null
+++ b/src/redux/cart/card.types.js
@@ -0,0 +1,8 @@
+const CartActionTypes = {
+ TOGGLE_CART_HIDDEN: "TOGGLE_CART_HIDDEN",
+ ADD_ITEM: "ADD_ITEM",
+ REMOVE_ITEM: "REMOVE_ITEM",
+ CLEAR_ITEM_FROM_CART: "CLEAR_ITEM_FROM_CART"
+};
+
+export default CartActionTypes;
diff --git a/src/redux/cart/cart.actions.js b/src/redux/cart/cart.actions.js
new file mode 100644
index 0000000..688f8ce
--- /dev/null
+++ b/src/redux/cart/cart.actions.js
@@ -0,0 +1,20 @@
+import CartActionTypes from './card.types'
+
+export const toggleCartHidden = () => ({
+ type: CartActionTypes.TOGGLE_CART_HIDDEN
+})
+
+export const addItem = item =>({
+ type: CartActionTypes.ADD_ITEM,
+ payload: item
+})
+
+export const removeItem = item =>({
+ type: CartActionTypes.REMOVE_ITEM,
+ payload: item
+})
+
+export const clearItemFromCart = item => ({
+ type: CartActionTypes.CLEAR_ITEM_FROM_CART,
+ payload: item
+})
diff --git a/src/redux/cart/cart.reducer.js b/src/redux/cart/cart.reducer.js
new file mode 100644
index 0000000..07dfa86
--- /dev/null
+++ b/src/redux/cart/cart.reducer.js
@@ -0,0 +1,43 @@
+import CartActionTypes from './card.types.js';
+import {addItemsToCart, removeItemFromCart} from './cart.utils.js';
+
+const INITIAL_STATE = {
+ hidden: true,
+ cartItems: []
+}
+
+const cartReducer = (state = INITIAL_STATE, action) => {
+ switch (action.type){
+
+ case CartActionTypes.TOGGLE_CART_HIDDEN:
+ return {
+ ...state,
+ hidden: !state.hidden
+ }
+
+ case CartActionTypes.ADD_ITEM:
+ return{
+ ...state,
+ cartItems: addItemsToCart(state.cartItems, action.payload )
+ }
+
+ case CartActionTypes.REMOVE_ITEM:
+ return{
+ ...state,
+ cartItems: removeItemFromCart(state.cartItems, action.payload)
+ }
+
+ case CartActionTypes.CLEAR_ITEM_FROM_CART:
+ return{
+ ...state,
+ cartItems: state.cartItems.filter(
+ cartItem => cartItem.id !== action.payload.id
+ )
+ }
+
+ default:
+ return state;
+ }
+}
+
+export default cartReducer;
\ No newline at end of file
diff --git a/src/redux/cart/cart.selectors.js b/src/redux/cart/cart.selectors.js
new file mode 100644
index 0000000..cfe4fe9
--- /dev/null
+++ b/src/redux/cart/cart.selectors.js
@@ -0,0 +1,34 @@
+import { createSelector } from 'reselect';
+
+const selectCart = state => state.cart;
+
+export const selectCartItems = createSelector(
+ [selectCart],
+ cart => cart.cartItems
+)
+
+export const selectCartHidden = createSelector(
+ [selectCart],
+ cart => cart.hidden
+)
+
+export const selectCartItemsCount = createSelector(
+ [selectCartItems],
+ cartItems =>
+ cartItems.reduce(
+ (accumulatedQuantity, cartItem) =>
+ accumulatedQuantity + cartItem.quantity,
+ 0
+ )
+)
+
+export const selectCartTotal = createSelector(
+ [selectCartItems],
+ cartItems =>
+ cartItems.reduce(
+ (accumulatedQuantity, cartItem) =>
+ accumulatedQuantity + cartItem.quantity * cartItem.price,
+ 0
+ )
+
+)
\ No newline at end of file
diff --git a/src/redux/cart/cart.utils.js b/src/redux/cart/cart.utils.js
new file mode 100644
index 0000000..2eaeff9
--- /dev/null
+++ b/src/redux/cart/cart.utils.js
@@ -0,0 +1,31 @@
+export const addItemsToCart = (cartItems, cartItemToAdd) => {
+ const existingCartItem = cartItems.find(
+ cartItem => cartItem.id === cartItemToAdd.id
+ )
+
+ if(existingCartItem){
+ return cartItems.map(cartItem =>
+ cartItem.id === cartItemToAdd.id
+ ? { ...cartItem, quantity: cartItem.quantity + 1 }
+ : cartItem
+ )
+ }
+
+ return [...cartItems, { ...cartItemToAdd, quantity: 1 }]
+}
+
+export const removeItemFromCart = (cartItems, cartItemToRemove) => {
+ const existingCartItem = cartItems.find(
+ cartItem => cartItem.id === cartItemToRemove.id
+ )
+
+ if(existingCartItem.quantity === 1){
+ return cartItems.filter(cartItem => cartItem.id !== cartItemToRemove.id)
+ }
+
+ return cartItems.map(cartItem =>
+ cartItem.id === cartItemToRemove.id
+ ? {...cartItem, quantity: cartItem.quantity -1 }
+ :cartItem
+ )
+}
\ No newline at end of file
diff --git a/src/redux/directory/directory.reducer.jsx b/src/redux/directory/directory.reducer.jsx
new file mode 100644
index 0000000..cd38020
--- /dev/null
+++ b/src/redux/directory/directory.reducer.jsx
@@ -0,0 +1,44 @@
+const INITIAL_STATE = {
+ sections: [
+ {
+ title: "موس (موشواره)",
+ imageUrl: "https://www.pngkey.com/png/full/957-9575055_redragon-m908-impact-mmo-gaming-mouse-up-to.png",
+ id: 1,
+ linkUrl: "shop/hats"
+ },
+ {
+ title: "کیبورد",
+ imageUrl: "https://www.pngplay.com/wp-content/uploads/13/Gaming-Keyboard-Transparent-Free-PNG.png",
+ id: 2,
+ linkUrl: "shop/jackets"
+ },
+ {
+ title: "وبکم",
+ imageUrl: "https://www.pngkey.com/png/full/246-2465497_webcam-transparent-images-trust-exis-webcam-17003.png",
+ id: 3,
+ linkUrl: "shop/sneakers"
+ },
+ {
+ title: "اسپیکر",
+ imageUrl: "https://www.pngarts.com/files/12/Portable-Bluetooth-Speaker-Transparent-Images.png",
+ size: "large",
+ id: 4,
+ linkUrl: "shop/womens"
+ },
+ {
+ title: "مانیتور",
+ imageUrl: "https://static.acer.com/up/Resource/Acer/Monitors/BE0/Image/20160503/BE240Y_sku_preview.png",
+ size: "large",
+ id: 5,
+ linkUrl: "shop/mens"
+ }
+ ]
+}
+const directoryReducer = (state = INITIAL_STATE, action) => {
+ switch(action.type){
+ default:
+ return state
+ }
+}
+
+export default directoryReducer
\ No newline at end of file
diff --git a/src/redux/directory/directory.selector.jsx b/src/redux/directory/directory.selector.jsx
new file mode 100644
index 0000000..7aee6df
--- /dev/null
+++ b/src/redux/directory/directory.selector.jsx
@@ -0,0 +1,8 @@
+import { createSelector } from 'reselect'
+
+const selectDirectory = state => state.directory
+
+export const selectDirectorySections = createSelector(
+ [selectDirectory],
+ directory => directory.sections
+)
\ No newline at end of file
diff --git a/src/redux/index.js b/src/redux/index.js
new file mode 100644
index 0000000..94cfa95
--- /dev/null
+++ b/src/redux/index.js
@@ -0,0 +1,7 @@
+// @create-index
+
+export { default as actions } from './actions';
+export { default as proxy } from './proxy.js';
+export { default as reducers } from './reducers';
+export { default as store } from './store.js';
+
diff --git a/src/redux/proxy.js b/src/redux/proxy.js
new file mode 100644
index 0000000..23bc497
--- /dev/null
+++ b/src/redux/proxy.js
@@ -0,0 +1,120 @@
+import { ApiConfig } from "../constants/defaultValues";
+import axios from "axios";
+
+let { baseUrl } = ApiConfig;
+let access = window.localStorage.getItem("access");
+baseUrl = baseUrl + "/";
+
+const Axios = axios.create({
+ withCredentials: true,
+ validateStatus: null,
+ baseURL: baseUrl,
+ //headers: access ? { Authorization: `Bearer ${access}` } : {},
+});
+class Proxy {
+ get = async (url, params, opt, data) =>
+ await this.check(
+ url,
+ opt,
+ async () => await Axios.get(url, { params, ...opt }),
+ data || params
+ );
+ post = async (url, params, opt, data ) =>
+ await this.check(
+ url,
+ opt,
+ async () => await Axios.post(url, params, opt),
+ data || params
+ );
+ put = async (url , params , opt , data ) =>
+ await this.check(
+ url,
+ opt,
+ async () => await Axios.put(url, params, opt),
+ data || params
+ );
+ delete = async (url , params , opt , data ) => {
+ await this.check(
+ url,
+ opt,
+ async () => await Axios.delete(url, { ...opt, data: params }),
+ data || params
+ );
+ };
+
+ check = async (url , { dispatch } , fetch , params ) => {
+ dispatch = dispatch || (() => {});
+ dispatch({ type: url.split("/")[0] + "/" + "loading" });
+ let response = await fetch();
+ switch (response.status) {
+ case 200:
+ dispatch({ type: url, data: response.data.data, params });
+ return response.data.data;
+ case 401:
+ if (await this.refresh()) {
+ let response = await fetch();
+ dispatch({ type: url, data: response.data.data, params });
+ return response.data.data;
+ }
+ break;
+ default:
+ dispatch({
+ type: url.split("/")[0] + "/" + "error",
+ data: response.data,
+ params,
+ });
+ }
+ return false;
+ };
+
+ refresh = async () => {
+ let refresh = localStorage.getItem("refresh");
+ if (!refresh) window.location.href = "/";
+ let login = await this.login(
+ "user/login",
+ {},
+ { headers: { Authorization: `Bearer ${refresh}` } }
+ );
+ return login ? true : false;
+ };
+ login = async (url , params , { dispatch } ) =>
+ this.post(url, params, {
+ dispatch: (obj ) => {
+ let login = obj.data;
+ if (!login || !login.refreshToken) return false;
+ localStorage.setItem("refresh", login.refreshToken);
+ localStorage.setItem("access", login.accessToken);
+ delete login.refreshToken;
+ delete login.accessToken;
+ localStorage.setItem("userData", JSON.stringify(login.profile));
+ dispatch(obj);
+ },
+ });
+
+ logout = async (url , params , { dispatch } ) => {
+ this.post(url, params, {
+ dispatch: (obj ) => {
+ localStorage.removeItem("refresh");
+ localStorage.removeItem("access");
+ localStorage.removeItem("userData");
+ dispatch(obj);
+ },
+ });
+ };
+ status = () => {
+ let refresh = localStorage.getItem("refresh");
+ let userData = localStorage.getItem("userData");
+ if (!refresh) return false;
+ if (refresh == "undefined") {
+ localStorage.removeItem("refresh");
+ localStorage.removeItem("access");
+ localStorage.removeItem("userData");
+ return false;
+ }
+
+ return JSON.parse(userData || '');
+ };
+}
+const _proxy = new Proxy();
+
+export default _proxy;
diff --git a/src/redux/reducers/blog.js b/src/redux/reducers/blog.js
new file mode 100644
index 0000000..f7fafc3
--- /dev/null
+++ b/src/redux/reducers/blog.js
@@ -0,0 +1,28 @@
+import { toast } from "react-toastify";
+
+const initialState = {
+ loading: false,
+ error: null,
+ list: [],
+};
+export default function blog(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ case "public/blogList":
+ return { ...state, loading: false, list: data, error: null };
+ // case "blog/update":
+ // return { ...state, loading: false, error: null };
+ // case "blog/add":
+ // return { ...state, loading: false, error: null };
+ // case "blog/delete":
+ // return { ...state, loading: false, error: null };
+ /////////////
+ case "blog/loading":
+ return { ...state, loading: true };
+ case "blog/error":
+ toast.error(data.message);
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/reducers/book.js b/src/redux/reducers/book.js
new file mode 100644
index 0000000..a29204a
--- /dev/null
+++ b/src/redux/reducers/book.js
@@ -0,0 +1,168 @@
+import { toast } from "react-toastify";
+
+const initialState = {
+ loading: false,
+ error: null,
+ list: [],
+ info: null,
+ searchResult: [],
+ filterResult: [],
+ subjects: [],
+ levels: [],
+ sortFilters: ["مرتب سازی", "محبوب ترین", "جدیدترین"],
+ selectedVideo: null,
+ gradeFilterBooks: null,
+ sortFilterCourse: null,
+ selectedGrade: null,
+ selectedSort: "پربازدید ترین ها",
+};
+
+const grades = [
+ "پیش دبستان",
+ "اول",
+ "دوم",
+ "سوم",
+ "چهارم",
+ "پنجم",
+ "ششم",
+ "هفتم",
+ "هشتم",
+ "نهم",
+ "دهم",
+ "یازدهم",
+ "دوازدهم",
+];
+
+export default function book(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ case "book/list":
+ let itemsSubject = (Array.isArray(data) ? data : []).map(
+ (item, index) => item.name
+ );
+ if (window.innerWidth < 1000) {
+ itemsSubject.unshift("نام کتاب");
+ }
+ let itemsLevel = (Array.isArray(data) ? data : [])
+ .map((item) => item.gradeId * 1)
+ .sort((a, b) => a - b)
+ .map((e) => grades[e]);
+ if (window.innerWidth < 1000) {
+ itemsLevel.unshift("پایه تحصیلی");
+ }
+ return {
+ ...state,
+ loading: false,
+ list: data,
+ subjects: Array.from(new Set(itemsSubject)),
+ levels: Array.from(new Set(itemsLevel)),
+ filterResult: data,
+ error: null,
+ };
+ case "book/info":
+ return { ...state, loading: false, info: data, error: null };
+ case "book/lesson/active":
+ return {
+ ...state,
+ loading: false,
+ error: null,
+ };
+ case "book/gradeFilter":
+ if (data === state.selectedGrade) {
+ return { ...state, gradeFilterBooks: null, selectedGrade: null };
+ } else {
+ return {
+ ...state,
+ gradeFilterBooks: state.list.filter(
+ (item, i) => grades[item.gradeId] === data
+ ),
+ selectedGrade: data,
+ };
+ }
+ case "book/sortFilter":
+ const search = state.list.filter(
+ (item) => item.name.indexOf(data.search) !== -1
+ );
+ if (data.sort === "تمامی دورهها") {
+ return { ...state, sortFilterCourse: search, selectedSort: data.sort };
+ } else if (data.sort === "جدیدترینها") {
+ return {
+ ...state,
+ sortFilterCourse: search.sort((item1, item2) => {
+ return item1.publishYear - item2.publishYear;
+ }),
+ selectedSort: data.sort,
+ };
+ }
+ return { ...state, sortFilterCourse: search, selectedSort: data.sort };
+
+ // case "book/update":
+ // return { ...state, loading: false, error: null };
+ // case "book/add":
+ // return { ...state, loading: false, error: null };
+ // case "book/delete":
+ // return { ...state, loading: false, error: null };
+ // case "book/search":
+ // return { ...state };
+ // case "book/filter":
+ // const realList = state.list;
+ // const level = data.level;
+ // const subject = data.subject;
+ // const sort = data.sort;
+ // const search = data.search;
+ // if (level.length > 0 || subject.length > 0 || sort || search) {
+ // localStorage.setItem("filter", "ON");
+ // }
+ // let finalSort;
+ // let byLevelFilter = [];
+ // if (level.length > 0) {
+ // level.map((item1) => {
+ // for (let i = 0; i < realList.length; i++) {
+ // if (grades[realList[i].gradeId] === item1) {
+ // byLevelFilter.push(realList[i]);
+ // }
+ // }
+ // });
+ // } else {
+ // byLevelFilter = realList;
+ // }
+
+ // let bySubjectFilter = [];
+ // if (subject.length > 0) {
+ // subject.map((item1) => {
+ // for (let i = 0; i < byLevelFilter.length; i++) {
+ // if (byLevelFilter[i].name === item1) {
+ // bySubjectFilter.push(byLevelFilter[i]);
+ // }
+ // }
+ // });
+ // } else {
+ // bySubjectFilter = byLevelFilter;
+ // }
+
+ // const bySearchFilter = search
+ // ? bySubjectFilter.filter((item1) => item1.name.indexOf(search) !== -1)
+ // : bySubjectFilter;
+
+ // if (sort === "گران ترین") {
+ // finalSort = bySearchFilter.sort((item1, item2) => {
+ // return item1.product[0].price - item2.product[0].price;
+ // });
+ // } else {
+ // finalSort = bySearchFilter;
+ // }
+ // return {
+ // ...state,
+ // loading: false,
+ // error: null,
+ // filterResult: finalSort,
+ // };
+ case "book/loading":
+ return { ...state, loading: true };
+ case "book/error":
+ toast.error(data.message);
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/reducers/faq.js b/src/redux/reducers/faq.js
new file mode 100644
index 0000000..c89b078
--- /dev/null
+++ b/src/redux/reducers/faq.js
@@ -0,0 +1,57 @@
+import { toast } from "react-toastify";
+
+const initialState = {
+ loading: false,
+ error: null,
+ list: [],
+ searchResult: [],
+ search: null,
+};
+export default function faq(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ case "faq/list":
+ return { ...state, loading: false, list: data, error: null };
+ case "faq/add":
+ return { ...state, loading: false, error: null };
+ case "faq/selectFaq":
+ return {
+ ...state,
+ loading: false,
+ error: null,
+ list: !state.search
+ ? state.list.map((item) =>
+ item.id === data
+ ? { ...item, active: !item.active }
+ : { ...item, active: false }
+ )
+ : state.list,
+ searchResult: state.search
+ ? state.searchResult.map((item) =>
+ item.id === data
+ ? { ...item, active: !item.active }
+ : { ...item, active: false }
+ )
+ : state.searchResult,
+ };
+ case "faq/search":
+ return {
+ ...state,
+ loading: false,
+ error: null,
+ search: data,
+ searchResult: state.list.filter(
+ (item) =>
+ item.title.indexOf(data) !== -1 ||
+ item.description.indexOf(data) !== -1
+ ),
+ };
+ case "faq/loading":
+ return { ...state, loading: true };
+ case "faq/error":
+ toast.error(data.message);
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/reducers/file.js b/src/redux/reducers/file.js
new file mode 100644
index 0000000..164494c
--- /dev/null
+++ b/src/redux/reducers/file.js
@@ -0,0 +1,26 @@
+const initialState = {
+ loading: false,
+ error: null,
+ uploads: {},
+ lastUpload: null,
+};
+
+export default function file(state = initialState, action) {
+ let { type, data, params } = action;
+ switch (type) {
+ case "file/upload":
+ return {
+ ...state,
+ loading: false,
+ error: null,
+ uploads: { ...state.uploads, [params?.target || "last"]: data.id },
+ lastUpload: data.id,
+ };
+ case "file/loading":
+ return { ...state, loading: true };
+ case "file/error":
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.js
new file mode 100644
index 0000000..584b6eb
--- /dev/null
+++ b/src/redux/reducers/index.js
@@ -0,0 +1,12 @@
+// @create-index
+export { default as publicApi } from "./public";
+export { default as user } from "./user";
+export { default as book } from "./book";
+export { default as blog } from "./blog";
+export { default as product } from "./product";
+export { default as faq } from "./faq";
+export { default as file } from "./file";
+export { default as userFactor } from "./userFactor";
+export { default as userProduct } from "./userProduct";
+export {default as scrollAction} from "./scroll";
+export {default as qr} from "./qr";
\ No newline at end of file
diff --git a/src/redux/reducers/product.js b/src/redux/reducers/product.js
new file mode 100644
index 0000000..4fb4099
--- /dev/null
+++ b/src/redux/reducers/product.js
@@ -0,0 +1,31 @@
+import { toast } from "react-toastify";
+
+const initialState = {
+ loading: false,
+ error: null,
+ listVOD: [],
+ selectedVOD: null,
+ similarList : [],
+};
+export default function product(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ case "product/listVOD":
+ return { ...state, loading: false, listVOD: data, error: null };
+ case "product/similar":
+ return { ...state, loading: false, similarList: data, error: null };
+ case "product/addVOD":
+ return { ...state, loading: false, error: null };
+ case "product/deleteVOD":
+ return { ...state, loading: false, error: null };
+ case "product/selectVOD":
+ return { ...state, selectedVOD: data };
+ case "product/loading":
+ return { ...state, loading: true };
+ case "product/error":
+ toast.error(data.message);
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/reducers/public.js b/src/redux/reducers/public.js
new file mode 100644
index 0000000..7e14a88
--- /dev/null
+++ b/src/redux/reducers/public.js
@@ -0,0 +1,184 @@
+import location from "../../utils/location";
+
+const initialState = {
+ isDark: false,
+ isMobile: window.innerWidth < 1024,
+ homeData: [],
+ newProducts: [],
+ levels: [],
+ loading: false,
+ error: null,
+ searchResult: [],
+ arList: [],
+ blogList: [],
+ blog: null,
+ province: null,
+ city: null,
+ departmans: [
+ {
+ id: 0,
+ name: "محتوا",
+ },
+ {
+ id: 0,
+ name: "دیزاین",
+ },
+ {
+ id: 0,
+ name: "برنامه نویسی",
+ },
+ ],
+ header: [],
+ selectedMenu: null,
+ contactData: null,
+ selectedVideo: null,
+ bookInfo: null,
+ activeCategory: null,
+ categories: null,
+ bookSection: null,
+ grades: {
+ 0: "پیش دبستان",
+ 1: "اول",
+ 2: "دوم",
+ 3: "سوم",
+ 4: "چهارم",
+ 5: "پنجم",
+ 6: "ششم",
+ 7: "هفتم",
+ 8: "هشتم",
+ 9: "نهم",
+ 10: "دهم",
+ 11: "یازدهم",
+ 12: "دوازدهم",
+ },
+ headerOptions: {
+ shown: false,
+ logo: false,
+ hamburgerMenu: false,
+ qr: false,
+ menu: false,
+ back: false,
+ title: false,
+ },
+ desktopContentTransform: false,
+};
+
+const grades = [
+ "پیش دبستان",
+ "اول",
+ "دوم",
+ "سوم",
+ "چهارم",
+ "پنجم",
+ "ششم",
+ "هفتم",
+ "هشتم",
+ "نهم",
+ "دهم",
+ "یازدهم",
+ "دوازدهم",
+];
+
+export default function publicApi(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ case "public/setDesktopContentTransform":
+ return { ...state, desktopContentTransform: data };
+ case "public/setHeaderOptions":
+ return { ...state, headerOptions: data };
+ case "public/setIsDark":
+ localStorage.setItem("isDark", data);
+ return { ...state, location: false, error: null, isDark: data };
+ case "active/video":
+ return {
+ ...state,
+ loading: false,
+ error: null,
+ selectedVideo: data,
+ };
+ case "active/category":
+ return {
+ ...state,
+ loading: false,
+ activeCategory: state.activeCategory === data ? null : data,
+ error: null,
+ };
+ case "public/vod/bookSection":
+ const videoId = location.getId();
+ return {
+ ...state,
+ loading: false,
+ bookSection: data,
+ error: null,
+ selectedVideo: data.vods?.filter((item) => item.id !== videoId)[0],
+ };
+
+ case "public/vod/bookCourse":
+ let allCats;
+ allCats = Array.from(
+ new Set(data.vods.map((item) => item.categoryId && item.categoryId))
+ );
+
+ return {
+ ...state,
+ loading: false,
+ bookInfo: {
+ ...data,
+ vods: data.vods.map((item) => {
+ return { ...item, active: false };
+ }),
+ },
+ error: null,
+ categories: allCats.map((item) =>
+ data.vods.filter((item1) => item1.categoryId === item)
+ ),
+ selectedVideo: data.vods.filter(
+ (item) => item.id !== location.getId()
+ )[0],
+ };
+ case "public/homePage":
+ let itemsLevel;
+ if (window.innerWidth > 1000) {
+ const jadidTarin = data.filter(
+ (item) => item.title === "جدیدترین کتابهای دانوین"
+ );
+ itemsLevel = jadidTarin[0].data
+ .map((item) => item.book.gradeId * 1)
+ .sort((a, b) => a - b)
+ .map((e) => grades[e]);
+ itemsLevel.unshift("همه");
+ }
+
+ return {
+ ...state,
+ loading: false,
+ levels: window.innerWidth > 1000 ? Array.from(new Set(itemsLevel)) : [],
+ error: null,
+ homeData: data,
+ };
+ case "public/ar":
+ return { ...state, loading: false, error: null, arList: data };
+ case "public/contact":
+ return { ...state, loading: false, error: null, contactData: data };
+ case "public/blogList":
+ return { ...state, loading: false, error: null, blogList: data };
+ case "public/blogInfo":
+ return { ...state, loading: false, error: null, blog: data };
+ case "public/setSubscribe":
+ return { ...state, loading: false, error: null, selectedSubscribe: data };
+ case "public/province":
+ return { ...state, loading: false, error: null, province: data };
+ case "public/city":
+ return { ...state, loading: false, error: null, city: data };
+ case "public/selectMenu":
+ return { ...state, loading: false, error: null, selectedMenu: data };
+ case "public/VOD/homePage":
+ return { ...state, loading: false, error: null, header: data };
+ case "loading":
+ return { ...state, loading: true };
+ case "error":
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/reducers/qr.js b/src/redux/reducers/qr.js
new file mode 100644
index 0000000..6b7e646
--- /dev/null
+++ b/src/redux/reducers/qr.js
@@ -0,0 +1,32 @@
+const initialState = {
+ loading: false,
+ error: null,
+ list: null,
+};
+export default function qr(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ case "qr/list":
+ return { ...state, loading: false, error: null };
+ case "qr/update":
+ return { ...state, loading: false, error: null };
+ case "qr/add":
+ return { ...state, loading: false, error: null };
+ case "qr/delete":
+ return { ...state, loading: false, error: null };
+ case "qr/info":
+ return {
+ ...state,
+ loading: false,
+ list: data,
+ error: null,
+ };
+ /////////////
+ case "loading":
+ return { ...state, loading: true };
+ case "error":
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
\ No newline at end of file
diff --git a/src/redux/reducers/scroll.js b/src/redux/reducers/scroll.js
new file mode 100644
index 0000000..5dfe9f0
--- /dev/null
+++ b/src/redux/reducers/scroll.js
@@ -0,0 +1,185 @@
+import scroll from "../../utils/scroll";
+const height = window.innerHeight;
+const width = window.innerWidth;
+const prevScroll = localStorage.getItem("prevScroll");
+
+const initialState = {
+ page1TopPosition: scroll.homeScrollHandler(1),
+ page2TopPosition: scroll.homeScrollHandler(2),
+ page2RotateX: scroll.linearFormula(
+ scroll.linearFormula(
+ prevScroll > height ? height : prevScroll,
+ 45,
+ 0,
+ 0,
+ height
+ )
+ ),
+ page2BoxPosition: scroll.initializeBoxPosition(prevScroll),
+ page2MobilePosition: scroll.linearFormula(
+ prevScroll > 2.5 * height ? 2.5 * height : prevScroll,
+ -(((height * 10) / 20 / width) * 100),
+ 6,
+ 2 * height,
+ 2.5 * height
+ ),
+ page2VideoPosition:
+ window.scrollY > 4 * height
+ ? scroll.videoLinearFormula(4 * height)
+ : window.scrollY > height
+ ? scroll.videoLinearFormula(window.scrollY)
+ : null,
+ page2VideoTopPosition:
+ prevScroll > 3 * height
+ ? scroll.linearFormula(3 * height, 70, 50, 2.8 * height, 3 * height)
+ : scroll.linearFormula(prevScroll, 70, 50, 2.8 * height, 3 * height),
+ page2MobileUnit: scroll.initializeMobileUnit(prevScroll),
+ videoTubeTopPosition: scroll.homeScrollHandler(3),
+ testTranslateY: scroll.linearFormula(
+ prevScroll > 5 * window.innerHeight ? 5 * window.innerHeight : prevScroll,
+ 0,
+ -101,
+ 4.5 * height,
+ 5 * height
+ ),
+ page3TopPosition: scroll.homeScrollHandler(4),
+ page3TestTranslateY:
+ prevScroll < 6.3 * height
+ ? 0
+ : scroll.linearFormula(
+ prevScroll > 7 * height ? 7 * height : prevScroll,
+ 0,
+ 100,
+ 6.3 * height,
+ 7 * height
+ ),
+ page4TopPosition: scroll.homeScrollHandler(5),
+};
+
+export default function scrollAction(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ case "scroll/page1/floatMouse":
+ return { ...state, floatMouseToggle: data };
+ case "scroll/page1/top":
+ return { ...state, page1TopPosition: scroll.homeScrollHandler(1) };
+ case "scroll/page2/top":
+ return { ...state, page2TopPosition: scroll.homeScrollHandler(2) };
+ case "scroll/page2/rotateX":
+ return {
+ ...state,
+ page2RotateX: scroll.linearFormula(window.scrollY, 45, 0, 0, height),
+ };
+ case "scroll/page2/boxPosition":
+ return {
+ ...state,
+ page2BoxPosition: {
+ translateY: scroll.linearFormula(
+ data.scrollY,
+ data.translateY.pos1,
+ data.translateY.pos2,
+ data.start,
+ data.end
+ ),
+ translateX: scroll.linearFormula(
+ data.scrollY,
+ data.translateX.pos1,
+ data.translateX.pos2,
+ data.start,
+ data.end
+ ),
+ rotate: scroll.linearFormula(
+ data.scrollY,
+ data.rotate.pos1,
+ data.rotate.pos2,
+ data.start,
+ data.end
+ ),
+ },
+ page2TopPosition: 0,
+ page2RotateX: 0,
+ };
+ case "scroll/page2/mobilePosition":
+ return {
+ ...state,
+ page2MobilePosition: scroll.linearFormula(
+ window.scrollY,
+ -(((height * 10) / 20 / width) * 100),
+ 6,
+ 2 * height,
+ 2.5 * height
+ ),
+ page2BoxPosition: { translateX: 32, translateY: 10, rotate: 0 },
+ };
+ case "scroll/page2/videoPosition":
+ return {
+ ...state,
+ page2VideoPosition: scroll.videoLinearFormula(window.scrollY),
+ page2VideoTopPosition: 50,
+ };
+ case "scroll/page2/videoTopPosition":
+ return {
+ ...state,
+ page2VideoTopPosition: scroll.linearFormula(
+ window.scrollY,
+ 70,
+ 50,
+ 2.8 * height,
+ 3 * height
+ ),
+ page2MobilePosition: 6,
+ };
+ case "scroll/page2/mobileUnit":
+ return {
+ ...state,
+ page2MobileUnit: scroll.initializeMobileUnit(window.scrollY),
+ };
+ case "scroll/videoTube/top":
+ return { ...state, videoTubeTopPosition: scroll.homeScrollHandler(3) };
+ case "scroll/test/translateY":
+ const joiRect = JSON.parse(localStorage.getItem("joiRect"));
+ return {
+ ...state,
+ testTranslateY: scroll.linearFormula(
+ window.scrollY,
+ 0,
+ -101,
+ 4.5 * height,
+ 5 * height
+ ),
+ videoTubeTopPosition: 0,
+ page2VideoPosition: {
+ width: joiRect.width,
+ height: joiRect.height,
+ top: joiRect.top,
+ left: joiRect.left,
+ },
+ };
+ case "scroll/page3/top":
+ return {
+ ...state,
+ page3TopPosition: scroll.homeScrollHandler(4),
+ testTranslateY: -101,
+ };
+ case "scroll/page3/testTranslateY":
+ return {
+ ...state,
+ page3TestTranslateY: scroll.linearFormula(
+ window.scrollY,
+ 0,
+ 100,
+ 6.3 * height,
+ 7 * height
+ ),
+ page3TopPosition: 0,
+ };
+ case "scroll/page4/top":
+ return {
+ ...state,
+ page4TopPosition: scroll.homeScrollHandler(5),
+ page3TestTranslateY: 100,
+ };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/reducers/user.js b/src/redux/reducers/user.js
new file mode 100644
index 0000000..3b6ff71
--- /dev/null
+++ b/src/redux/reducers/user.js
@@ -0,0 +1,49 @@
+import proxy from "../proxy";
+import { toast } from "react-toastify";
+
+const initialState = {
+ status: proxy.status(),
+ loading: false,
+ error: null,
+ list: null,
+ domains: [],
+ mothers: [],
+ profileStatus: false,
+ setDone: false,
+ setLogin : false,
+};
+export default function user(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ // case "user/login":
+ // case "user/register":
+ // case "user/switchRole":
+ case "public/sendOTP":
+ return { ...state, loading: false, status: data.profile, error: null };
+ case "user/login":
+ case "user/otp/login":
+ return {
+ ...state,
+ loading: false,
+ status: data.profile,
+ error: null,
+ setLogin: !state.setLogin,
+ };
+ case "user/getProfile":
+ localStorage.setItem("userData", JSON.stringify(data));
+ return { ...state, loading: false, status: data, error: null, setDone : false, };
+ case "user/logout":
+ return { ...state, loading: false, status: proxy.status(), error: null };
+ case "user/getUserRole":
+ return { ...state, loading: false, userRoles: data, error: null };
+ case "user/setProfile":
+ toast.success("تغییرات اعمال شد.");
+ return { ...state, loading: false, error: null, setDone : true };
+ case "loading":
+ return { ...state, loading: true };
+ case "error":
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/reducers/userFactor.js b/src/redux/reducers/userFactor.js
new file mode 100644
index 0000000..ae95fa3
--- /dev/null
+++ b/src/redux/reducers/userFactor.js
@@ -0,0 +1,57 @@
+import { toast } from "react-toastify";
+const initialState = {
+ loading: false,
+ error: null,
+ list: null,
+ info: null,
+ sum: null,
+ checkEmpty: false,
+ isVerified: null,
+};
+export default function userFactor(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ //dnvn
+ case "userFactor/list":
+ const checkEmpty = data.filter(
+ (item) => item.condition === 2 && item.product.productType === (1 || 3)
+ );
+ return {
+ ...state,
+ loading: false,
+ list: data,
+ checkEmpty: checkEmpty.length > 0 ? false : true,
+ error: null,
+ };
+ case "userFactor/info":
+ return { ...state, loading: false, info: data, error: null };
+ case "userFactor/add":
+ toast.success("درخواست شما با موفقیت انجام شد.");
+ return { ...state, loading: false, error: null, sum: data.payPrice };
+ case "userFactor/delete":
+ return { ...state, loading: false, error: null };
+ case "userFactor/payment":
+ window.location.replace(data);
+ return { ...state, loading: false, error: null };
+ //vod
+ case "userFactor/myList":
+ return { ...state, loading: false, list: data, error: null };
+ case "userFactor/update":
+ toast.success("درخواست شما با موفقیت انجام شد.");
+ return { ...state, loading: false, error: null };
+ case "userFactor/deleteUserOffCode":
+ return { ...state, loading: false, error: null };
+ case "userFactor/paymentVOD":
+ window.location.replace(data);
+ return { ...state, loading: false, error: null, isVerified: false };
+ case "userFactor/verifyVOD":
+ return { ...state, loading: false, error: null, isVerified: true };
+ case "userFactor/loading":
+ return { ...state, loading: true };
+ case "userFactor/error":
+ toast.error(data.message);
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/reducers/userProduct.js b/src/redux/reducers/userProduct.js
new file mode 100644
index 0000000..80ade2d
--- /dev/null
+++ b/src/redux/reducers/userProduct.js
@@ -0,0 +1,50 @@
+import { toast } from "react-toastify";
+
+const initialState = {
+ loading: false,
+ error: null,
+ list: [],
+ info: null,
+ sum: null,
+ checkEmpty: false,
+ hasPhysical: false,
+};
+export default function userFactor(state = initialState, action) {
+ let { type, data } = action;
+ switch (type) {
+ case "userProduct/list":
+ const checkEmpty = data.filter(
+ (item) => item.condition === 2 && item.product.productType === (1 || 3)
+ );
+ const hasPhysical =
+ data.filter((item) => item.product.productType === 2).length > 0
+ ? true
+ : false;
+ return {
+ ...state,
+ loading: false,
+ list: data,
+ checkEmpty: checkEmpty.length > 0 ? false : true,
+ error: null,
+ hasPhysical: hasPhysical,
+ };
+ case "userProduct/info":
+ return { ...state, loading: false, info: data, error: null };
+ case "userProduct/update":
+ return { ...state, loading: false, error: null };
+ case "userProduct/add":
+ if (window.location.pathname.indexOf("products") !== -1) {
+ toast.success("درخواست شما با موفقیت انجام شد.");
+ }
+ return { ...state, loading: false, error: null, sum: data.payPrice };
+ case "userProduct/delete":
+ return { ...state, loading: false, error: null };
+ case "userProduct/loading":
+ return { ...state, loading: true };
+ case "userProduct/error":
+ toast.error(data.message);
+ return { ...state, loading: false, error: data.message };
+ default:
+ return state;
+ }
+}
diff --git a/src/redux/root-reducer.js b/src/redux/root-reducer.js
new file mode 100644
index 0000000..bf6870c
--- /dev/null
+++ b/src/redux/root-reducer.js
@@ -0,0 +1,24 @@
+import { combineReducers } from 'redux'
+import { persistReducer } from 'redux-persist'
+import storage from 'redux-persist/lib/storage'
+
+import userReducer from './user/user.reducer.js'
+import cartReducer from './cart/cart.reducer.js'
+import directoryReducer from './directory/directory.reducer'
+import shopReducer from './shop/shop.reducer'
+
+
+const persistConfig = {
+ key: 'root',
+ storage,
+ whitelist: ['cart']
+}
+
+const rootReducer = combineReducers({
+ user: userReducer,
+ cart: cartReducer,
+ directory: directoryReducer,
+ shop: shopReducer,
+})
+
+export default persistReducer(persistConfig, rootReducer)
\ No newline at end of file
diff --git a/src/redux/shop/shop.data.js b/src/redux/shop/shop.data.js
new file mode 100644
index 0000000..ff369bf
--- /dev/null
+++ b/src/redux/shop/shop.data.js
@@ -0,0 +1,254 @@
+const SHOP_DATA = [
+ {
+ id: 1,
+ title: "mouse",
+ routeName: "mouse",
+ items: [
+ {
+ id: 1,
+ name: "ماوس ریزر مدل DEATHADDER ESSENTIAL RZ01",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/114510799.jpg",
+ price: 73000,
+ adress: '/products/razor-mouse-deathadder-rz01',
+ description: {
+ content: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ، و با استفاده از طراحان گرافیک است، چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است، و برای شرایط فعلی تکنولوژی مورد نیاز، و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد، کتابهای زیادی در شصت و سه درصد گذشته حال و آینده، شناخت فراوان جامعه و متخصصان را می طلبد، تا با نرم افزارها شناخت بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری موجود در ارائه راهکارها، و شرایط سخت تایپ به پایان رسد و زمان مورد نیاز شامل حروفچینی دستاوردهای اصلی، و جوابگوی سوالات پیوسته اهل دنیای موجود طراحی اساسا مورد استفاده قرار گیرد.',
+ }
+ },
+ {
+ id: 2,
+ name: "ماوس مخصوص بازی مدل V8",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/eddb26c7043d4092e17ba4e05a19f5b49f20f0dc_1633724598.jpg",
+ price: 290000
+ },
+ {
+ id: 3,
+ name: "ماوس مخصوص بازی زویا مدل MMR1",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/2c9f4388ced87e860a9c9d0fccc595f3b64a55a2_1610015683.jpg",
+ price: 210000
+ },
+ {
+ id: 4,
+ name: "ماوس مخصوص بازی تسکو مدل TSCO TM 764 GA",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/d0cc25319e2fce02acb42f679ff282b59182ed54_1597813753.jpg",
+ price: 186000
+ },
+ {
+ id: 5,
+ name: "ماوس مخصوص بازی مدل GM02",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/33b0985d01fe4b1ec2542bd03f3b7998e68981ed_1636141537.jpg",
+ price: 297270
+ },
+ {
+ id: 6,
+ name: "ماوس بی سیم ریزر مدل Basilisk Ultimate",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/81ad2dd69067d43c967ed76faf928837629ece45_1630752955.jpg",
+ price: 5250000
+ },
+ {
+ id: 7,
+ name: "ماوس مخصوص بازی ریزر مدل Viper Mini",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/878005cff2b58c0d0d0ac4e9ca7eaa21d3987c2c_1600681192.jpg",
+ price: 1460000
+ },
+ {
+ id: 8,
+ name: "ماوس تسکو مدل TM 291",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/27e2b2852f53615b577fc9130b97675cb77067ba_1628667044.jpg",
+ price: 83800
+ },
+ {
+ id: 9,
+ name: "ماوس آرمو مدل M25",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/4b757355ca6dcae804f34d4c6af66305903b379c_1596460030.jpg",
+ price: 160000
+ }
+ ]
+ },
+ {
+ id: 2,
+ title: "وبکم",
+ routeName: "webcam",
+ items: [
+ {
+ id: 10,
+ name: "وب کم ایکس اچ سی مدل 48O",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/6f167d1f3b0aa40e19760d5f5240912e00e31dc9_1632987698.jpg",
+ price: '340,000'
+ },
+ {
+ id: 11,
+ name: "وب کم مدل PGR-008",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/3fa4c49aed640c53560fd32a117c7685923123db_1615139913.jpg",
+ price: '2,070,000'
+ },
+ {
+ id: 12,
+ name: "وب کم مایکروسافت مدل لایف کم سینما",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/32558.jpg",
+ price: 110
+ },
+ {
+ id: 13,
+ name: "وب کم ای فورتک مدل PK-635G",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/2190153c71b9fd3ba9f191f23fa767d8b2cd9200_1601221909.jpg",
+ price: '630,000'
+ },
+ {
+ id: 14,
+ name: "وب کم HD مایکروسافت مدل لایف کم HD-3000",
+ imageUrl: "https://dkstatics-public.digikala.com/digikala-products/15382.jpg",
+ price: '1,300,000'
+ },
+ // {
+ // id: 15,
+ // name: "Nike Brown High Tops",
+ // imageUrl: "https://i.ibb.co/fMTV342/nike-brown.png",
+ // price: 160
+ // },
+ // {
+ // id: 16,
+ // name: "Air Jordan Limited",
+ // imageUrl: "https://i.ibb.co/w4k6Ws9/nike-funky.png",
+ // price: 190
+ // },
+ // {
+ // id: 17,
+ // name: "Timberlands",
+ // imageUrl: "https://i.ibb.co/Mhh6wBg/timberlands.png",
+ // price: 200
+ // }
+ ]
+ },
+ {
+ id: 3,
+ title: "Jackets",
+ routeName: "jackets",
+ items: [
+ {
+ id: 18,
+ name: "Black Jean Shearling",
+ imageUrl: "https://i.ibb.co/XzcwL5s/black-shearling.png",
+ price: 125
+ },
+ {
+ id: 19,
+ name: "Blue Jean Jacket",
+ imageUrl: "https://i.ibb.co/mJS6vz0/blue-jean-jacket.png",
+ price: 90
+ },
+ {
+ id: 20,
+ name: "Grey Jean Jacket",
+ imageUrl: "https://i.ibb.co/N71k1ML/grey-jean-jacket.png",
+ price: 90
+ },
+ {
+ id: 21,
+ name: "Brown Shearling",
+ imageUrl: "https://i.ibb.co/s96FpdP/brown-shearling.png",
+ price: 165
+ },
+ {
+ id: 22,
+ name: "Tan Trench",
+ imageUrl: "https://i.ibb.co/M6hHc3F/brown-trench.png",
+ price: 185
+ }
+ ]
+ },
+ {
+ id: 4,
+ title: "Womens",
+ routeName: "womens",
+ items: [
+ {
+ id: 23,
+ name: "Blue Tanktop",
+ imageUrl: "https://i.ibb.co/7CQVJNm/blue-tank.png",
+ price: 25
+ },
+ {
+ id: 24,
+ name: "Floral Blouse",
+ imageUrl: "https://i.ibb.co/4W2DGKm/floral-blouse.png",
+ price: 20
+ },
+ {
+ id: 25,
+ name: "Floral Dress",
+ imageUrl: "https://i.ibb.co/KV18Ysr/floral-skirt.png",
+ price: 80
+ },
+ {
+ id: 26,
+ name: "Red Dots Dress",
+ imageUrl: "https://i.ibb.co/N3BN1bh/red-polka-dot-dress.png",
+ price: 80
+ },
+ {
+ id: 27,
+ name: "Striped Sweater",
+ imageUrl: "https://i.ibb.co/KmSkMbH/striped-sweater.png",
+ price: 45
+ },
+ {
+ id: 28,
+ name: "Yellow Track Suit",
+ imageUrl: "https://i.ibb.co/v1cvwNf/yellow-track-suit.png",
+ price: 135
+ },
+ {
+ id: 29,
+ name: "White Blouse",
+ imageUrl: "https://i.ibb.co/qBcrsJg/white-vest.png",
+ price: 20
+ }
+ ]
+ },
+ {
+ id: 5,
+ title: "Mens",
+ routeName: "mens",
+ items: [
+ {
+ id: 30,
+ name: "Camo Down Vest",
+ imageUrl: "https://i.ibb.co/xJS0T3Y/camo-vest.png",
+ price: 325
+ },
+ {
+ id: 31,
+ name: "Floral T-shirt",
+ imageUrl: "https://i.ibb.co/qMQ75QZ/floral-shirt.png",
+ price: 20
+ },
+ {
+ id: 32,
+ name: "Black & White Longsleeve",
+ imageUrl: "https://i.ibb.co/55z32tw/long-sleeve.png",
+ price: 25
+ },
+ {
+ id: 33,
+ name: "Pink T-shirt",
+ imageUrl: "https://i.ibb.co/RvwnBL8/pink-shirt.png",
+ price: 25
+ },
+ {
+ id: 34,
+ name: "Jean Long Sleeve",
+ imageUrl: "https://i.ibb.co/VpW4x5t/roll-up-jean-shirt.png",
+ price: 40
+ },
+ {
+ id: 35,
+ name: "Burgundy T-shirt",
+ imageUrl: "https://i.ibb.co/mh3VM1f/polka-dot-shirt.png",
+ price: 25
+ }
+ ]
+ }
+];
+
+export default SHOP_DATA;
+
diff --git a/src/redux/shop/shop.reducer.js b/src/redux/shop/shop.reducer.js
new file mode 100644
index 0000000..2042117
--- /dev/null
+++ b/src/redux/shop/shop.reducer.js
@@ -0,0 +1,14 @@
+import SHOP_DATA from './shop.data';
+
+const INITIAL_STATE = {
+ collections: SHOP_DATA
+};
+
+const shopReducer = (state = INITIAL_STATE, action) => {
+ switch (action.type) {
+ default:
+ return state;
+ }
+};
+
+export default shopReducer;
\ No newline at end of file
diff --git a/src/redux/shop/shop.selectors.js b/src/redux/shop/shop.selectors.js
new file mode 100644
index 0000000..ee1470d
--- /dev/null
+++ b/src/redux/shop/shop.selectors.js
@@ -0,0 +1,8 @@
+import { createSelector } from 'reselect'
+
+const selectShop = state => state.shop
+
+export const selectCollections = createSelector(
+ [selectShop],
+ shop => shop.collections
+);
diff --git a/src/redux/store.js b/src/redux/store.js
new file mode 100644
index 0000000..ecaf6db
--- /dev/null
+++ b/src/redux/store.js
@@ -0,0 +1,14 @@
+import { createStore, applyMiddleware } from 'redux';
+import { composeWithDevTools } from 'redux-devtools-extension';
+import { combineReducers } from 'redux';
+import * as reducers from './reducers';
+import thunk from 'redux-thunk';
+const initialState = {};
+const middleware = [thunk];
+const store = createStore(
+ combineReducers(reducers),
+ initialState,
+ composeWithDevTools(applyMiddleware(...middleware))
+);
+export default store;
+
diff --git a/src/redux/user/user.actions.js b/src/redux/user/user.actions.js
new file mode 100644
index 0000000..2e8df2c
--- /dev/null
+++ b/src/redux/user/user.actions.js
@@ -0,0 +1,6 @@
+import { UserActionTypes } from './user.types.js'
+
+export const setCurrentUser = user => ({
+ type: UserActionTypes.SET_CURRENT_USER,
+ payload: user
+})
\ No newline at end of file
diff --git a/src/redux/user/user.reducer.js b/src/redux/user/user.reducer.js
new file mode 100644
index 0000000..c15b079
--- /dev/null
+++ b/src/redux/user/user.reducer.js
@@ -0,0 +1,19 @@
+import { UserActionTypes } from './user.types.js'
+
+const INITIAL_STATE = {
+ currentUser: null
+}
+
+const userReducer = (state = INITIAL_STATE, action ) => {
+ switch(action.type){
+ case UserActionTypes.SET_CURRENT_USER:
+ return{
+ ...state,
+ currentUser: action.payload
+ }
+ default:
+ return state;
+ }
+}
+
+export default userReducer;
\ No newline at end of file
diff --git a/src/redux/user/user.selector.js b/src/redux/user/user.selector.js
new file mode 100644
index 0000000..40ba3f9
--- /dev/null
+++ b/src/redux/user/user.selector.js
@@ -0,0 +1,9 @@
+import { createSelector } from 'reselect';
+
+const selectUser = state => state.user;
+
+export const selectCurrentUser = createSelector(
+ [selectUser],
+ user => user.currentUser
+)
+
diff --git a/src/redux/user/user.types.js b/src/redux/user/user.types.js
new file mode 100644
index 0000000..86c4fc9
--- /dev/null
+++ b/src/redux/user/user.types.js
@@ -0,0 +1,3 @@
+export const UserActionTypes = {
+ SET_CURRENT_USER: "SET_CURRENT_USER"
+};
\ No newline at end of file
diff --git a/src/utils/comments.js b/src/utils/comments.js
new file mode 100644
index 0000000..5b6cfd8
--- /dev/null
+++ b/src/utils/comments.js
@@ -0,0 +1,22 @@
+export const connectToParent = (list) => {
+ let comments = [];
+ for (let item of list) {
+ comments.push({ ...item, child: [] });
+ }
+
+ for (let comment1 of comments) {
+ if (comment1.pid !== null) {
+ const index = comments.indexOf(
+ comments.filter((comment2) => comment2.id === comment1.pid)[0]
+ );
+ comments[index] = {
+ ...comments[index],
+ child: [...comments[index].child, comment1],
+ };
+ } else {
+
+ }
+ }
+ // comments = comments.filter((comment) => comment.pid === null);
+ console.log(comments);
+};
diff --git a/src/utils/location.js b/src/utils/location.js
new file mode 100644
index 0000000..aa083ce
--- /dev/null
+++ b/src/utils/location.js
@@ -0,0 +1,11 @@
+
+class location {
+ getId = () => {
+ const pathname = window.location.pathname;
+ return pathname.slice(pathname.lastIndexOf('/') + 1, pathname.length);
+ }
+}
+
+const _location = new location();
+
+export default _location;
\ No newline at end of file
diff --git a/src/utils/onInput.js b/src/utils/onInput.js
new file mode 100644
index 0000000..0a45958
--- /dev/null
+++ b/src/utils/onInput.js
@@ -0,0 +1,282 @@
+import { toast } from "react-toastify";
+
+class onInput {
+ persianOnly = (txt) => {
+ let newText = txt;
+ newText = newText.replace(/`/g, "");
+ newText = newText.replace(/0/g, "");
+ newText = newText.replace(/1/g, "");
+ newText = newText.replace(/2/g, "");
+ newText = newText.replace(/3/g, "");
+ newText = newText.replace(/4/g, "");
+ newText = newText.replace(/5/g, "");
+ newText = newText.replace(/6/g, "");
+ newText = newText.replace(/7/g, "");
+ newText = newText.replace(/8/g, "");
+ newText = newText.replace(/9/g, "");
+ newText = newText.replace(/0/g, "");
+ newText = newText.replace(/q/g, "");
+ newText = newText.replace(/w/g, "");
+ newText = newText.replace(/e/g, "");
+ newText = newText.replace(/r/g, "");
+ newText = newText.replace(/t/g, "");
+ newText = newText.replace(/y/g, "");
+ newText = newText.replace(/u/g, "");
+ newText = newText.replace(/i/g, "");
+ newText = newText.replace(/o/g, "");
+ newText = newText.replace(/p/g, "");
+ newText = newText.replace(/\[/g, "");
+ newText = newText.replace(/\]/g, "");
+ newText = newText.replace(/a/g, "");
+ newText = newText.replace(/s/g, "");
+ newText = newText.replace(/d/g, "");
+ newText = newText.replace(/f/g, "");
+ newText = newText.replace(/g/g, "");
+ newText = newText.replace(/h/g, "");
+ newText = newText.replace(/j/g, "");
+ newText = newText.replace(/k/g, "");
+ newText = newText.replace(/l/g, "");
+ newText = newText.replace(/;/g, "");
+ newText = newText.replace(/'/g, "");
+ newText = newText.replace(/z/g, "");
+ newText = newText.replace(/x/g, "");
+ newText = newText.replace(/c/g, "");
+ newText = newText.replace(/v/g, "");
+ newText = newText.replace(/b/g, "");
+ newText = newText.replace(/n/g, "");
+ newText = newText.replace(/m/g, "");
+ newText = newText.replace(/,/g, "");
+ newText = newText.replace(/./g, "");
+ newText = newText.replace(/\//g, "");
+ newText = newText.replace(/~/g, " ");
+ newText = newText.replace(/Q/g, "");
+ newText = newText.replace(/W/g, "");
+ newText = newText.replace(/E/g, "");
+ newText = newText.replace(/R/g, "");
+ newText = newText.replace(/T/g, "");
+ newText = newText.replace(/Y/g, "");
+ newText = newText.replace(/U/g, "");
+ newText = newText.replace(/I/g, "");
+ newText = newText.replace(/O/g, "");
+ newText = newText.replace(/P/g, "");
+ newText = newText.replace(/A/g, "");
+ newText = newText.replace(/S/g, "");
+ newText = newText.replace(/G/g, "");
+ newText = newText.replace(/H/g, "");
+ newText = newText.replace(/J/g, "");
+ newText = newText.replace(/K/g, "");
+ newText = newText.replace(/L/g, "");
+ newText = newText.replace(/Z/g, "");
+ newText = newText.replace(/X/g, "");
+ newText = newText.replace(/B/g, "");
+ newText = newText.replace(/N/g, "");
+ newText = newText.replace(/M/g, "");
+ newText = newText.replace(/\?/g, "");
+ if (!newText && newText.length !== 0) {
+ toast.info("از کاراکتر فارسی استفاده کنید");
+ }
+ return newText;
+ };
+ englishAndNumberWithoutSpace = (txt) => {
+ let newText = txt;
+ newText = newText.replace(/`/g, "");
+ newText = newText.replace(/0/g, "0");
+ newText = newText.replace(/1/g, "1");
+ newText = newText.replace(/2/g, "2");
+ newText = newText.replace(/3/g, "3");
+ newText = newText.replace(/4/g, "4");
+ newText = newText.replace(/5/g, "5");
+ newText = newText.replace(/6/g, "6");
+ newText = newText.replace(/7/g, "7");
+ newText = newText.replace(/8/g, "8");
+ newText = newText.replace(/9/g, "9");
+ newText = newText.replace(/ا/g, "");
+ newText = newText.replace(/ب/g, "");
+ newText = newText.replace(/پ/g, "");
+ newText = newText.replace(/ت/g, "");
+ newText = newText.replace(/ث/g, "");
+ newText = newText.replace(/ج/g, "");
+ newText = newText.replace(/چ/g, "");
+ newText = newText.replace(/ح/g, "");
+ newText = newText.replace(/خ/g, "");
+ newText = newText.replace(/د/g, "");
+ newText = newText.replace(/\[/g, "");
+ newText = newText.replace(/\]/g, "");
+ newText = newText.replace(/ذ/g, "");
+ newText = newText.replace(/ر/g, "");
+ newText = newText.replace(/ز/g, "");
+ newText = newText.replace(/ژ/g, "");
+ newText = newText.replace(/ط/g, "");
+ newText = newText.replace(/ظ/g, "");
+ newText = newText.replace(/ع/g, "");
+ newText = newText.replace(/غ/g, "");
+ newText = newText.replace(/ف/g, "");
+ newText = newText.replace(/;/g, "");
+ newText = newText.replace(/'/g, "");
+ newText = newText.replace(/ق/g, "");
+ newText = newText.replace(/س/g, "");
+ newText = newText.replace(/ش/g, "");
+ newText = newText.replace(/ص/g, "");
+ newText = newText.replace(/ض/g, "");
+ newText = newText.replace(/ک/g, "");
+ newText = newText.replace(/گ/g, "");
+ newText = newText.replace(/,/g, "");
+ newText = newText.replace(/\./g, "");
+ newText = newText.replace(/\//g, "");
+ newText = newText.replace(/~/g, " ");
+ newText = newText.replace(/ل/g, "");
+ newText = newText.replace(/م/g, "");
+ newText = newText.replace(/ن/g, "");
+ newText = newText.replace(/و/g, "");
+ newText = newText.replace(/ه/g, "");
+ newText = newText.replace(/ی/g, "");
+ newText = newText.replace(/\?/g, "");
+ if (!newText && newText.length !== 0) {
+ toast.info("از کاراکتر انگلیسی واعداداستفاده کنید");
+ }
+ return newText;
+ };
+ numberOnly = (txt) => {
+ let newText = txt;
+ newText = newText.replace(/`/g, "");
+ newText = newText.replace(/0/g, "0");
+ newText = newText.replace(/1/g, "1");
+ newText = newText.replace(/2/g, "2");
+ newText = newText.replace(/3/g, "3");
+ newText = newText.replace(/4/g, "4");
+ newText = newText.replace(/5/g, "5");
+ newText = newText.replace(/6/g, "6");
+ newText = newText.replace(/7/g, "7");
+ newText = newText.replace(/8/g, "8");
+ newText = newText.replace(/9/g, "9");
+ newText = newText.replace(/ا/g, "");
+ newText = newText.replace(/ب/g, "");
+ newText = newText.replace(/پ/g, "");
+ newText = newText.replace(/ت/g, "");
+ newText = newText.replace(/ث/g, "");
+ newText = newText.replace(/ج/g, "");
+ newText = newText.replace(/چ/g, "");
+ newText = newText.replace(/ح/g, "");
+ newText = newText.replace(/خ/g, "");
+ newText = newText.replace(/د/g, "");
+ newText = newText.replace(/\[/g, "");
+ newText = newText.replace(/\]/g, "");
+ newText = newText.replace(/ذ/g, "");
+ newText = newText.replace(/ر/g, "");
+ newText = newText.replace(/ز/g, "");
+ newText = newText.replace(/ژ/g, "");
+ newText = newText.replace(/ط/g, "");
+ newText = newText.replace(/ظ/g, "");
+ newText = newText.replace(/ع/g, "");
+ newText = newText.replace(/غ/g, "");
+ newText = newText.replace(/ف/g, "");
+ newText = newText.replace(/;/g, "");
+ newText = newText.replace(/'/g, "");
+ newText = newText.replace(/ق/g, "");
+ newText = newText.replace(/س/g, "");
+ newText = newText.replace(/ش/g, "");
+ newText = newText.replace(/ص/g, "");
+ newText = newText.replace(/ض/g, "");
+ newText = newText.replace(/ک/g, "");
+ newText = newText.replace(/گ/g, "");
+ newText = newText.replace(/\,/g, "");
+ newText = newText.replace(/\./g, "");
+ newText = newText.replace(/\//g, "");
+ newText = newText.replace(/~/g, " ");
+ newText = newText.replace(/ل/g, "");
+ newText = newText.replace(/م/g, "");
+ newText = newText.replace(/ن/g, "");
+ newText = newText.replace(/و/g, "");
+ newText = newText.replace(/ه/g, "");
+ newText = newText.replace(/ی/g, "");
+ newText = newText.replace(/\?/g, "");
+ newText = newText.replace(/q/g, "");
+ newText = newText.replace(/w/g, "");
+ newText = newText.replace(/e/g, "");
+ newText = newText.replace(/r/g, "");
+ newText = newText.replace(/t/g, "");
+ newText = newText.replace(/y/g, "");
+ newText = newText.replace(/u/g, "");
+ newText = newText.replace(/i/g, "");
+ newText = newText.replace(/o/g, "");
+ newText = newText.replace(/p/g, "");
+ newText = newText.replace(/a/g, "");
+ newText = newText.replace(/s/g, "");
+ newText = newText.replace(/d/g, "");
+ newText = newText.replace(/f/g, "");
+ newText = newText.replace(/g/g, "");
+ newText = newText.replace(/h/g, "");
+ newText = newText.replace(/j/g, "");
+ newText = newText.replace(/k/g, "");
+ newText = newText.replace(/l/g, "");
+ newText = newText.replace(/z/g, "");
+ newText = newText.replace(/x/g, "");
+ newText = newText.replace(/c/g, "");
+ newText = newText.replace(/v/g, "");
+ newText = newText.replace(/b/g, "");
+ newText = newText.replace(/n/g, "");
+ newText = newText.replace(/m/g, "");
+ newText = newText.replace(/Q/g, "");
+ newText = newText.replace(/W/g, "");
+ newText = newText.replace(/E/g, "");
+ newText = newText.replace(/R/g, "");
+ newText = newText.replace(/T/g, "");
+ newText = newText.replace(/Y/g, "");
+ newText = newText.replace(/U/g, "");
+ newText = newText.replace(/I/g, "");
+ newText = newText.replace(/O/g, "");
+ newText = newText.replace(/P/g, "");
+ newText = newText.replace(/A/g, "");
+ newText = newText.replace(/S/g, "");
+ newText = newText.replace(/G/g, "");
+ newText = newText.replace(/H/g, "");
+ newText = newText.replace(/J/g, "");
+ newText = newText.replace(/K/g, "");
+ newText = newText.replace(/L/g, "");
+ newText = newText.replace(/Z/g, "");
+ newText = newText.replace(/X/g, "");
+ newText = newText.replace(/B/g, "");
+ newText = newText.replace(/N/g, "");
+ newText = newText.replace(/M/g, "");
+ if (!newText && newText.length !== 0) {
+ toast.info("از اعداداستفاده کنید");
+ }
+ return newText;
+ };
+ dayHandler = (num) => {
+ let newNum = this.numberOnly(num);
+ if (newNum > 31) {
+ toast.error("روز نباید بزرگتر از 31 باشد.");
+ return Math.floor(newNum / 10);
+ }
+ return newNum;
+ };
+ monthHandler = (num) => {
+ let newNum = this.numberOnly(num);
+ if (newNum > 12 || newNum < 0) {
+ toast.error("ماه باید بین 1 تا 12 باشد.");
+ return Math.floor(newNum / 10);
+ }
+ return newNum;
+ };
+ yearHandler = (num) => {
+ let newNum = this.numberOnly(num);
+ if (newNum > 1395) {
+ toast.error("سال باید کوچکتر از 1395 باشد.");
+ return Math.floor(newNum / 10);
+ }
+ return newNum;
+ };
+ phonenumber = (value) => {
+ let formattedValue = value
+ .replace(/[\u0660-\u0669\u06f0-\u06f9]/g, function (c) {
+ return c.charCodeAt(0) & 0xf;
+ })
+ .replace(/[^\d]/, "");
+ return formattedValue;
+ };
+}
+
+const _onInput = new onInput();
+
+export default _onInput;
diff --git a/src/utils/scroll.js b/src/utils/scroll.js
new file mode 100644
index 0000000..61b1486
--- /dev/null
+++ b/src/utils/scroll.js
@@ -0,0 +1,213 @@
+class scroll {
+ goToTop = () => {
+ window.scrollTo(0, 0);
+ };
+
+ homeScrollHandler = (index) => {
+ const prevScroll = Number(localStorage.getItem('prevScroll'));
+ const height = Number(window.innerHeight);
+ const scrollY = Number(window.scrollY);
+ if (index === 1) {
+ if (scrollY === 0) {
+ return 0;
+ }else
+ if (scrollY > 0 && scrollY <= height) {
+ return -scrollY;
+ }
+ if (scrollY > height) {
+ return -height;
+ }
+ } else if (index === 2) {
+ if (scrollY === 0) {
+ return height;
+ }
+ if (scrollY > 0 && scrollY <= height) {
+ return height - scrollY;
+ }
+ if (scrollY > 10 * height) {
+ return -1 * height;
+ }
+ } else if (index === 3) {
+ if (scrollY <= 3.5 * height) {
+ return height;
+ }
+ if (scrollY > 3.5 * height && scrollY <= 4 * height) {
+ return 4 * height - scrollY;
+ }
+ if (scrollY > 10 * height) {
+ return -height;
+ }
+ } else if (index === 4) {
+ if (scrollY <= 5 * height) {
+ return height;
+ } else if (scrollY > 5 * height && scrollY < 6 * height) {
+ return 6 * height - scrollY;
+ }
+ if (scrollY === 10 * height) {
+ return -height;
+ }
+ } else if (index === 5) {
+ if (scrollY <= 7.3 * height) {
+ return height;
+ } else if (scrollY > 7.3 * height && scrollY < 8.3 * height) {
+ return 8.3 * height - scrollY;
+ }
+ if (scrollY === 10 * height) {
+ return -height;
+ }
+ }
+ };
+
+ linearFormula = (currentScroll, position1, position2, scroll1, scroll2) => {
+ const alpha = (currentScroll - scroll1) / (scroll2 - scroll1);
+ const deltaPosition = position2 - position1;
+ const currentPosition = position1 + deltaPosition * alpha;
+ return currentPosition;
+ };
+
+ videoLinearFormula = (currentScroll) => {
+ if (!localStorage.getItem("htmlRect")) {
+ localStorage.setItem(
+ "htmlRect",
+ JSON.stringify(document.getElementById("video").getBoundingClientRect())
+ );
+ }
+ const htmlRect =
+ JSON.parse(localStorage.getItem("htmlRect")) ||
+ document.getElementById("video").getBoundingClientRect();
+ const joiRect = JSON.parse(localStorage.getItem("joiRect"));
+
+ const currentWidth = this.linearFormula(
+ currentScroll,
+ htmlRect.width,
+ joiRect.width,
+ 3.5 * window.innerHeight,
+ 4 * window.innerHeight
+ );
+ const currentHeight = this.linearFormula(
+ currentScroll,
+ htmlRect.height,
+ joiRect.height,
+ 3.5 * window.innerHeight,
+ 4 * window.innerHeight
+ );
+ const currentLeft = this.linearFormula(
+ currentScroll,
+ htmlRect.left,
+ joiRect.left,
+ 3.5 * window.innerHeight,
+ 4 * window.innerHeight
+ );
+
+ const currentTop = this.linearFormula(
+ currentScroll,
+ htmlRect.top,
+ joiRect.top - window.innerHeight,
+ 3.5 * window.innerHeight,
+ 4 * window.innerHeight
+ );
+ return {
+ width: currentWidth,
+ height: currentHeight,
+ top: currentTop,
+ left: currentLeft,
+ };
+ };
+
+ testLinearFolmula = (currentScroll) => {
+ const testRect = JSON.parse(localStorage.getItem("testRect"));
+ const currentWidth = this.linearFormula(
+ currentScroll,
+ testRect.width,
+ window.innerWidth,
+ 4.5 * window.innerHeight,
+ 5 * window.innerHeight
+ );
+ const currentHeight = this.linearFormula(
+ currentScroll,
+ testRect.height,
+ window.innerHeight,
+ 4.5 * window.innerHeight,
+ 5 * window.innerHeight
+ );
+ const currentLeft = this.linearFormula(
+ currentScroll,
+ testRect.left,
+ 0,
+ 4.5 * window.innerHeight,
+ 5 * window.innerHeight
+ );
+
+ const currentTop = this.linearFormula(
+ currentScroll,
+ testRect.top,
+ 0,
+ 4.5 * window.innerHeight,
+ 5 * window.innerHeight
+ );
+
+ return {
+ width: currentWidth,
+ height: currentHeight,
+ top: currentTop,
+ left: currentLeft,
+ };
+ };
+
+ initializeBoxPosition = (currentScroll) => {
+ const height= window.innerHeight;
+ if(currentScroll < height){
+ console.log(currentScroll);
+ return {
+ translateX: -5,
+ translateY: -5,
+ rotate: 5
+ }
+ }
+ if(currentScroll > height && currentScroll < 2 * height){
+ return {
+ translateX: this.linearFormula(currentScroll, -5, 32, height, 2 * height),
+ translateY: this.linearFormula(currentScroll, -5, 10, height, 2 * height),
+ rotate: this.linearFormula(currentScroll, 5, 0, height, 2 * height)
+ }
+ }
+ if(currentScroll > 2 * height && currentScroll <= 2.5 * height){
+ return {
+ translateX: 32,
+ translateY: 10,
+ rotate: 0
+ }
+ }
+ if(currentScroll > 2.5 * height && currentScroll <= 3 * height){
+ return {
+ translateX: this.linearFormula(currentScroll, 32, 32, 2.5 * height, 3 * height),
+ translateY: this.linearFormula(currentScroll, 10, -30, 2.5 * height, 3 * height),
+ rotate: this.linearFormula(currentScroll, 0, 0, 2.5 * height, 3 * height)
+ }
+ }
+ if(currentScroll > 3 * height){
+ return {
+ translateX: 32,
+ translateY: -30,
+ rotate: 0
+ }
+ }
+ };
+
+ initializeMobileUnit = (currentScroll) => {
+ const height = window.innerHeight;
+ if (currentScroll > 2 * height && currentScroll <= 2.3 * height) {
+ return 1;
+ } else if (currentScroll > 2.3 * height && currentScroll <= 2.7 * height) {
+ return 2;
+ } else if (currentScroll > 2.7 * height && currentScroll <= 2.8 * height) {
+ return 3;
+ } else if (currentScroll > 2.8 * height && currentScroll < 3.5 * height) {
+ return 4;
+ }
+ }
+}
+
+const _scroll = new scroll();
+
+export default _scroll;
diff --git a/src/view/services/index.js b/src/view/services/index.js
index a43a3b7..7d18964 100644
--- a/src/view/services/index.js
+++ b/src/view/services/index.js
@@ -1,18 +1,83 @@
-import React from "react";
+import React, {useEffect, useState} from "react";
import Class from "../../components/products/classes";
-import PLATFORM_DATA from "../../store";
-const Services = () => {
+import { book, publicApi } from "../../redux/actions";
+import { connect } from 'react-redux';
+import { Link } from "react-router-dom";
+import Product from '../../components/Product';
+const Services = ({ isMobile,
+ books,
+ getList,
+ grades,
+ gradeFilter,
+ selectedGrade,
+ gradeFilterBooks,
+ setHeaderOptions,
+ headerOptions,
+ videos,
+ }) => {
+
+ const [filter, setFilter] = useState({
+ search: "",
+ grade: "",
+ productsType: "کتاب",
+ });
+
+
+ const onChange = (name, value) => {
+ setFilter({ ...filter, [name]: value });
+ };
+
+ useEffect(() => {
+ getList();
+ setHeaderOptions(headerOptions);
+ }, []);
+
+ useEffect(() => {
+ gradeFilter(filter.grade);
+ }, [filter]);
+
+
return(
-
- {
- PLATFORM_DATA.classes.map((item, idx) => (
-
- ))
- }
+
+ { books?.map((product, index) => (
+
+
+
+ ))}
);
};
-export default Services;
\ No newline at end of file
+const mapStateToProps = (state) => ({
+ isDark: state.publicApi.isDark,
+ isMobile: state.publicApi.isMobile,
+ books: state.book.list,
+ grades: state.publicApi.grades,
+ gradeFilterBooks: state.book.gradeFilterBooks,
+ selectedGrade: state.book.selectedGrade,
+ });
+
+ const mapDispatchToProps = {
+ getList: book.list,
+ gradeFilter: book.gradeFilter,
+ setHeaderOptions: publicApi.setHeaderOptions,
+ };
+
+ export default connect(mapStateToProps, mapDispatchToProps)(Services);
+
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index b6a8ee0..ccc2f24 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2400,6 +2400,13 @@
"resolved" "https://registry.npmjs.org/axe-core/-/axe-core-4.3.5.tgz"
"version" "4.3.5"
+"axios@^0.25.0":
+ "integrity" "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g=="
+ "resolved" "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz"
+ "version" "0.25.0"
+ dependencies:
+ "follow-redirects" "^1.14.7"
+
"axobject-query@^2.2.0":
"integrity" "sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA=="
"resolved" "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz"
@@ -2899,6 +2906,11 @@
"resolved" "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz"
"version" "1.0.4"
+"clsx@^1.1.1":
+ "integrity" "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
+ "resolved" "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz"
+ "version" "1.1.1"
+
"co@^4.6.0":
"integrity" "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
"resolved" "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
@@ -4343,10 +4355,10 @@
"resolved" "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz"
"version" "3.2.4"
-"follow-redirects@^1.0.0":
- "integrity" "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A=="
- "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz"
- "version" "1.14.6"
+"follow-redirects@^1.0.0", "follow-redirects@^1.14.7":
+ "integrity" "sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA=="
+ "resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz"
+ "version" "1.14.8"
"fork-ts-checker-webpack-plugin@^6.5.0":
"integrity" "sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw=="
@@ -7318,7 +7330,7 @@
"strip-ansi" "^6.0.1"
"text-table" "^0.2.0"
-"react-dom@*", "react-dom@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0", "react-dom@^17.0.2", "react-dom@>=16.8":
+"react-dom@*", "react-dom@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0", "react-dom@^17.0.2", "react-dom@>=16", "react-dom@>=16.8":
"integrity" "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA=="
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
"version" "17.0.2"
@@ -7456,7 +7468,14 @@
"@tweenjs/tween.js" "^18.1.2"
"resize-observer-polyfill" "^1.5.1"
-"react@*", "react@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0", "react@^16.8.3 || ^17", "react@^17.0.2", "react@>= 16", "react@>= 16.8", "react@>=16.8", "react@17.0.2":
+"react-toastify@^8.1.1":
+ "integrity" "sha512-KQa+j+JHjy69xpPYheaWUq3SjVw1sV2D7/QsXW6Tpo2x9HhUCvASezAMYDmLhfAYoSnaXUVnTNFAgZQA75SExQ=="
+ "resolved" "https://registry.npmjs.org/react-toastify/-/react-toastify-8.1.1.tgz"
+ "version" "8.1.1"
+ dependencies:
+ "clsx" "^1.1.1"
+
+"react@*", "react@^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0", "react@^16.8.3 || ^17", "react@^17.0.2", "react@>= 16", "react@>= 16.8", "react@>=16", "react@>=16.8", "react@17.0.2":
"integrity" "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA=="
"resolved" "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
"version" "17.0.2"
@@ -7508,7 +7527,17 @@
"indent-string" "^4.0.0"
"strip-indent" "^3.0.0"
-"redux@^4.0.0", "redux@^4.1.2":
+"redux-devtools-extension@^2.13.9":
+ "integrity" "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A=="
+ "resolved" "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz"
+ "version" "2.13.9"
+
+"redux-thunk@^2.4.1":
+ "integrity" "sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q=="
+ "resolved" "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.1.tgz"
+ "version" "2.4.1"
+
+"redux@^3.1.0 || ^4.0.0", "redux@^4", "redux@^4.0.0", "redux@^4.1.2":
"integrity" "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw=="
"resolved" "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz"
"version" "4.1.2"