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 }) => ( +
+
+ { + cartItems.length ? ( + cartItems.map(cartItem => ( + + )) + ):( + سبد خرید شما خالیست. + )} +
+
+
+
+) + +const mapStateToProps = createStructuredSelector({ + cartItems: selectCartItems +}) + +export default withRouter(connect(mapStateToProps)(CartDropdown)); \ No newline at end of file diff --git a/src/components/Product/discount.png b/src/components/Product/discount.png new file mode 100644 index 0000000..eff7853 Binary files /dev/null and b/src/components/Product/discount.png differ diff --git a/src/components/Product/goldon.png b/src/components/Product/goldon.png new file mode 100644 index 0000000..d864e44 Binary files /dev/null and b/src/components/Product/goldon.png differ diff --git a/src/components/Product/index.js b/src/components/Product/index.js new file mode 100644 index 0000000..4993f08 --- /dev/null +++ b/src/components/Product/index.js @@ -0,0 +1,276 @@ +import React, { useState } from "react"; + +// import Tag from "../Tag"; +// import ReactTooltip from "react-tooltip"; + +import book from "./goldon.png"; +import watermelon from "./watermelon.png"; +import heart from "./vuesax-linear-heart.svg"; +import discountImg from "./discount.png"; +import YellowDotIcon from "./yellowDotIcon.svg"; +import RedDotIcon from "./redDotIcon.svg"; +import PurpleDotIcon from "./purpleDotIcon.svg"; +import LightBlueDotIcon from "./lightBlueDotIcon.svg"; + + +const ProductDesign = ({ + colors, + likeBtn, + discount, + discountPricePercent, + discountPrice, + productContent, + star, + productTags, + priceDirection, + productTitleAlignment, + priceHolder, + productHolderBorder, + productHolderBorderColor, + productTitleColor, + productExcerptTextColor, + productCat, + productCatTextColor, + rangeHolder, + verticalImg, + horizontalImg, + rangeWidth, + addToCartHolder, + productName, + productPrice, + productImg, + writeGHEIMAT, + numberOfRegards, + productHolderHoverBorder, + hoverMode, + big, + starRate +}) => { + const [isHovering, setIsHovering] = useState(false); + return ( +
+
+
setIsHovering(true)} + onMouseLeave={() => setIsHovering(false)} + > + {/* product image */} +
+ {/* icons on image */} +
+ {discountPricePercent && ( +
+

+ 23% +

+
+ )} + {likeBtn && ( +
+ +
+ )} + {discount && ( +
+ +
+ )} +
+ {/* img */} +
+ {verticalImg && ( + + )} + {horizontalImg && ( + + )} +
+ {/* select Color */} + {/* {colors && ( +
+ + + + + + + + +
+ )} */} + +
+ {/* product content */} +
+
+

+ {productName} +

+ {productContent && ( +

+ این کتاب شامل گلدان و گیاه و خاک می باشد و همچنین آپشن های خوب + دیگری نیز دارد. +

+ )} + {productCat && ( + + {productCat} + + )} +
+ {/* star */} + {/* price */} + {priceHolder && ( +
+
+
+ {writeGHEIMAT && قیمت} + {/* price with line on it */} + {discountPrice && ( +

+ 145تومان +

+ )} +
+

+ {productPrice} تومان +

+
+ {/* add to cart */} + {addToCartHolder && ( + + + )} + + +
+ + )} + + + + {hoverMode == 'blue' ?
+
+ +
+
: null} + + {/* {productTags && ( +
+ { + productTags.map((item, index) => ) + } +
+ )} */} + {rangeHolder && ( +
+
+
+ )} + +
+
+
+
+ ); +}; + +export default ProductDesign; + +// text colors i used for product title: +// text-darkCrimsonTextColor +// text-productTitle + +// text color i used for product excerpt: +// text-productContent + +// text color i used for product cat: +// text-darkCrimsonTextColor + +ProductDesign.defaultProps = { + // icons on product image + colors: false, + likeBtn: false, + discount: false, + discountPricePercent: false, + // product price + discountPrice: false, + priceHolder: true, + + productContent: false, + star: true, + productTags: [], + productCat: true, + rangeHolder: true, + + // دو مدل عکس طراحی کردم که یکی عرض بیشتری دارد یکی ارتفاع + verticalImg: true, + horizontalImg: false, + + addToCartHolder: false, + + productTitleColor: "text-productTitle", + + productExcerptTextColor: "text-productContent", + + productCatTextColor: "text-darkCrimsonTextColor", + // برای درصد دادن به عرض رنج + rangeWidth: "w-44", + //دی ای وی که محصول داخلش است میتونیم بهش بوردر بدیم + productHolderBorder: "border", + productHolderBorderColor: "border-grayBorderColor", + + productTitleAlignment: "text-justify", + //میتونیم تعیین کنیم که قیمت و دکمه اضاف به سبد خرید کنار یکدیگر باشند یا زیر هم + priceDirection: "flex-row", + productImg: book, + writeGHEIMAT: false +}; diff --git a/src/components/Product/lightBlueDotIcon.svg b/src/components/Product/lightBlueDotIcon.svg new file mode 100644 index 0000000..fc3cc88 --- /dev/null +++ b/src/components/Product/lightBlueDotIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Product/purpleDotIcon.svg b/src/components/Product/purpleDotIcon.svg new file mode 100644 index 0000000..1859629 --- /dev/null +++ b/src/components/Product/purpleDotIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Product/redDotIcon.svg b/src/components/Product/redDotIcon.svg new file mode 100644 index 0000000..1c20427 --- /dev/null +++ b/src/components/Product/redDotIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Product/vuesax-linear-heart.svg b/src/components/Product/vuesax-linear-heart.svg new file mode 100644 index 0000000..306bf40 --- /dev/null +++ b/src/components/Product/vuesax-linear-heart.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/components/Product/watermelon.png b/src/components/Product/watermelon.png new file mode 100644 index 0000000..fb5efe7 Binary files /dev/null and b/src/components/Product/watermelon.png differ diff --git a/src/components/Product/yellowDotIcon.svg b/src/components/Product/yellowDotIcon.svg new file mode 100644 index 0000000..bd9e33e --- /dev/null +++ b/src/components/Product/yellowDotIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/products/classes/index.js b/src/components/products/classes/index.js index 3e35aa1..4773626 100644 --- a/src/components/products/classes/index.js +++ b/src/components/products/classes/index.js @@ -1,18 +1,28 @@ -import React from "react"; -const Class = ({ - title, - description, - teacher, - group, - price, -}) => { - return( -
-

{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"