drawer - some api - headerOption added

temp
Reza_ashrafi 3 years ago
parent f309ac09a0
commit fb80e688ab
  1. 1
      package.json
  2. 3
      src/App.js
  3. 8
      src/assets/icons/bookmark-deactive-dark.svg
  4. 8
      src/assets/icons/bookmark-deactive-light.svg
  5. 10
      src/assets/icons/dots-menu-dark.svg
  6. 10
      src/assets/icons/dots-menu-light.svg
  7. 8
      src/components/BottomNavigation/index.js
  8. 14
      src/components/Button/index.js
  9. 17
      src/components/Drawer/index.js
  10. 4
      src/components/Header/index.js
  11. 74
      src/components/Navbar/index.js
  12. 14
      src/redux/actions/public.js
  13. 13
      src/redux/reducers/public.js
  14. 85
      src/router.js
  15. 20
      src/views/Auth/SignUp/index.js
  16. 31
      src/views/Auth/index.js
  17. 2
      src/views/Home/Portrait/Private/Blogs/index.js
  18. 4
      src/views/Home/Portrait/Private/MyBooks/index.js
  19. 2
      src/views/Home/Portrait/Private/Videos/index.js
  20. 9
      src/views/Home/Portrait/index.js
  21. 24
      src/views/Home/index.js
  22. 40
      src/views/Product/index.js
  23. 6
      src/views/Products/index.js
  24. 31
      src/views/ShoppingCart/Code/index.js
  25. 72
      src/views/ShoppingCart/Product/index.js
  26. 28
      src/views/ShoppingCart/index.js
  27. 5
      yarn.lock

@ -15,6 +15,7 @@
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-modern-drawer": "^1.0.1",
"react-redux": "^7.2.6", "react-redux": "^7.2.6",
"react-router-dom": "^6.0.2", "react-router-dom": "^6.0.2",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",

@ -2,6 +2,7 @@ import "./App.scss";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { ToastContainer } from "react-toastify"; import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css"; import "react-toastify/dist/ReactToastify.css";
import "react-modern-drawer/dist/index.css";
import store from "./redux/store"; import store from "./redux/store";
import Router from "./router"; import Router from "./router";
@ -12,7 +13,7 @@ function App({theme}) {
<ToastContainer <ToastContainer
position="top-right" position="top-right"
theme="colored" theme="colored"
autoClose={2000} autoClose={4000}
hideProgressBar={true} hideProgressBar={true}
newestOnTop={true} newestOnTop={true}
closeOnClick closeOnClick

@ -0,0 +1,8 @@
<svg id="vuesax_linear_archive-add" data-name="vuesax/linear/archive-add" xmlns="http://www.w3.org/2000/svg" width="29.778" height="29.778" viewBox="0 0 29.778 29.778">
<g id="archive-add" transform="translate(0)">
<path id="Vector" d="M6.2,0H0" transform="translate(11.787 13.214)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
<path id="Vector-2" data-name="Vector" d="M0,0V6.2" transform="translate(14.889 10.187)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
<path id="Vector-3" data-name="Vector" d="M16.75,0H4.789A4.8,4.8,0,0,0,0,4.789V22.272c0,2.233,1.6,3.176,3.561,2.1l6.055-3.362a2.628,2.628,0,0,1,2.32,0l6.055,3.362c1.96,1.092,3.561.149,3.561-2.1V4.789A4.819,4.819,0,0,0,16.75,0Z" transform="translate(4.119 2.482)" fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
<path id="Vector-4" data-name="Vector" d="M0,0H29.778V29.778H0Z" fill="none" opacity="0"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,8 @@
<svg id="vuesax_linear_archive-add" data-name="vuesax/linear/archive-add" xmlns="http://www.w3.org/2000/svg" width="29.778" height="29.778" viewBox="0 0 29.778 29.778">
<g id="archive-add" transform="translate(0)">
<path id="Vector" d="M6.2,0H0" transform="translate(11.787 13.214)" fill="none" stroke="#196ec0" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
<path id="Vector-2" data-name="Vector" d="M0,0V6.2" transform="translate(14.889 10.187)" fill="none" stroke="#196ec0" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
<path id="Vector-3" data-name="Vector" d="M16.75,0H4.789A4.8,4.8,0,0,0,0,4.789V22.272c0,2.233,1.6,3.176,3.561,2.1l6.055-3.362a2.628,2.628,0,0,1,2.32,0l6.055,3.362c1.96,1.092,3.561.149,3.561-2.1V4.789A4.819,4.819,0,0,0,16.75,0Z" transform="translate(4.119 2.482)" fill="none" stroke="#196ec0" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"/>
<path id="Vector-4" data-name="Vector" d="M0,0H29.778V29.778H0Z" fill="none" opacity="0"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="29.778" height="29.778" viewBox="0 0 29.778 29.778">
<g id="vuesax_linear_more" data-name="vuesax/linear/more" transform="translate(29.778) rotate(90)">
<g id="more">
<path id="Vector" d="M2.482,0A2.482,2.482,0,1,0,4.963,2.482,2.489,2.489,0,0,0,2.482,0Z" transform="translate(3.722 12.408)" fill="#fff"/>
<path id="Vector-2" data-name="Vector" d="M2.482,0A2.482,2.482,0,1,0,4.963,2.482,2.489,2.489,0,0,0,2.482,0Z" transform="translate(21.093 12.408)" fill="#fff"/>
<path id="Vector-3" data-name="Vector" d="M2.482,0A2.482,2.482,0,1,0,4.963,2.482,2.489,2.489,0,0,0,2.482,0Z" transform="translate(12.408 12.408)" fill="#fff"/>
<path id="Vector-4" data-name="Vector" d="M0,0H29.778V29.778H0Z" transform="translate(0 0)" fill="#fff" opacity="0"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 843 B

@ -0,0 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" width="29.778" height="29.778" viewBox="0 0 29.778 29.778">
<g id="vuesax_linear_more" data-name="vuesax/linear/more" transform="translate(29.778) rotate(90)">
<g id="more">
<path id="Vector" d="M2.482,0A2.482,2.482,0,1,0,4.963,2.482,2.489,2.489,0,0,0,2.482,0Z" transform="translate(3.722 12.408)" fill="#196ec0"/>
<path id="Vector-2" data-name="Vector" d="M2.482,0A2.482,2.482,0,1,0,4.963,2.482,2.489,2.489,0,0,0,2.482,0Z" transform="translate(21.093 12.408)" fill="#196ec0"/>
<path id="Vector-3" data-name="Vector" d="M2.482,0A2.482,2.482,0,1,0,4.963,2.482,2.489,2.489,0,0,0,2.482,0Z" transform="translate(12.408 12.408)" fill="#196ec0"/>
<path id="Vector-4" data-name="Vector" d="M0,0H29.778V29.778H0Z" transform="translate(0 0)" fill="#196ec0" opacity="0"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 855 B

@ -1,5 +1,5 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { Link } from "react-router-dom"; import { NavLink } from "react-router-dom";
import { connect } from "react-redux"; import { connect } from "react-redux";
import activeHomeIcon from "../../assets/icons/bottom-home-active.svg"; import activeHomeIcon from "../../assets/icons/bottom-home-active.svg";
import deactiveProductsIcon from "../../assets/icons/bottom-products-deactive.svg"; import deactiveProductsIcon from "../../assets/icons/bottom-products-deactive.svg";
@ -12,7 +12,7 @@ export const BottomNavigation = ({ pages, isDark }) => {
const Tab = ({ page }) => { const Tab = ({ page }) => {
return ( return (
<Link <NavLink
to={page.link} to={page.link}
className="flex flex-col justify-center itemms-center" className="flex flex-col justify-center itemms-center"
onClick={() => setActiveLink(page.link)} onClick={() => setActiveLink(page.link)}
@ -25,14 +25,14 @@ export const BottomNavigation = ({ pages, isDark }) => {
<span className="text-blueB9 text-xs text-center font-bold mt-1"> <span className="text-blueB9 text-xs text-center font-bold mt-1">
{page.name} {page.name}
</span> </span>
</Link> </NavLink>
); );
}; };
return ( return (
<> <>
<footer <footer
className="w-full flex flex-row justify-around items-center fixed bottom-0 left-0 py-2 z-20 lg:hidden" className="w-full flex flex-row justify-around items-center fixed bottom-0 left-0 py-2 z-20 lg:hidden"
style={{ background: isDark ? "hsla(209, 39%, 28%, 1)" : "#E8FCFF" }} style={{ background: isDark ? "hsla(209, 39%, 28%, 0.96)" : "#E8FCFFEE" }}
> >
{pages.map((page, index) => ( {pages.map((page, index) => (
<Tab page={page} key={index} /> <Tab page={page} key={index} />

@ -8,12 +8,22 @@ export default function Button({
width, width,
bgOpacity, bgOpacity,
onClick, onClick,
selectable,
active,
}) { }) {
return ( return (
<button <button
className={`rounded-md py-3 justify-center text-sm ${backgroundColor} ${color} ${bgOpacity}`} className={`rounded-md py-3 justify-center text-sm ${
selectable ? (active ? "bg-black" : backgroundColor) : backgroundColor
} ${selectable ? (active ? "text-blueC0" : color) : color} ${
selectable ? (active ? "bg-opacity-10" : bgOpacity) : bgOpacity
} `}
style={{ style={{
border: `${border.width} solid ${border.color}`, border: `${
selectable ? (active ? "2px" : border.width) : border.width
} solid ${
selectable ? (active ? "#196EC0" : border.color) : border.color
}`,
width: width, width: width,
}} }}
onClick={() => onClick()} onClick={() => onClick()}

@ -0,0 +1,17 @@
import React from "react";
import Drawer from "react-modern-drawer";
const MyDrawer = ({ drawerOpen, setDrawerOpen }) => {
return (
<Drawer
open={drawerOpen}
onClose={() => setDrawerOpen(() => !drawerOpen)}
size={'80%'}
direction="right"
>
</Drawer>
);
};
export default MyDrawer;

@ -10,11 +10,11 @@ function Header({ title, link, isDark }) {
<strong className="text-blueC0 dark:text-white font-semibold text-sm"> <strong className="text-blueC0 dark:text-white font-semibold text-sm">
{title} {title}
</strong> </strong>
<Link to={link}> <Link to={link} className="ml-1">
<img <img
src={isDark ? arrowWhiteIcon : arrowBlueIcon} src={isDark ? arrowWhiteIcon : arrowBlueIcon}
alt="" alt=""
className={`transform ${isDark ? "w-5" : "w-5 rotate-180"}`} className={`transform ${isDark ? "w-6" : "w-6 rotate-180"}`}
/> />
</Link> </Link>
</header> </header>

@ -1,7 +1,9 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { connect } from "react-redux"; import { connect } from "react-redux";
import _ from "lodash"; import _ from "lodash";
import Drawer from "../Drawer";
//assets
import grayBagIcon from "../../assets/icons/gray-bag.svg"; import grayBagIcon from "../../assets/icons/gray-bag.svg";
import graySearchIcon from "../../assets/icons/gray-search.svg"; import graySearchIcon from "../../assets/icons/gray-search.svg";
import logoIcon from "../../assets/icons/logo.svg"; import logoIcon from "../../assets/icons/logo.svg";
@ -11,9 +13,20 @@ import arIcon from "../../assets/icons/ar-white.svg";
import arBlueIcon from "../../assets/icons/ar-blue.svg"; import arBlueIcon from "../../assets/icons/ar-blue.svg";
import hamburgerIcon from "../../assets/icons/hamburger.svg"; import hamburgerIcon from "../../assets/icons/hamburger.svg";
import hamburgerBlueIcon from "../../assets/icons/hamburger-blue.svg"; import hamburgerBlueIcon from "../../assets/icons/hamburger-blue.svg";
import arrowBlueIcon from "../../assets/icons/arrow-right-blue.svg";
import arrowWhiteIcon from "../../assets/icons/arrow-left-white.svg";
import dotsMenuDarkIcon from "../../assets/icons/dots-menu-dark.svg";
import bookmarkDeactiveDarkIcon from "../../assets/icons/bookmark-deactive-dark.svg";
import dotsMenuLightIcon from "../../assets/icons/dots-menu-light.svg";
import bookmarkDeactiveLightIcon from "../../assets/icons/bookmark-deactive-light.svg";
function Navbar({ pages, isDark, headerOptions }) {
const navigation = useNavigate();
const { logo, hamburgerMenu, qr, shown, menu, title, back, bookmark } =
headerOptions;
function Navbar({ pages, isDark}) {
const [top, setTop] = useState(false); const [top, setTop] = useState(false);
const [drawerOpen, setDrawerOpen] = useState(false);
useEffect(() => { useEffect(() => {
if (window.scrollY === 0) { if (window.scrollY === 0) {
setTop(() => true); setTop(() => true);
@ -65,23 +78,45 @@ function Navbar({ pages, isDark}) {
</div> </div>
{/* landscape end */} {/* landscape end */}
{/* portrait start */} {/* portrait start */}
<div className="w-full h-full relative flex flex-row items-center justify-between lg:hidden pt-6 pb-2 px-4"> <div className="w-full h-full relative flex flex-row items-center justify-between lg:hidden py-3 px-4">
<div className="relative"> <div className="relative">
{hamburgerMenu && (
<>
<span className="w-2.5 h-2.5 rounded-full bg-redFF1 absolute -left-1 -top-1 animate-bounce"></span> <span className="w-2.5 h-2.5 rounded-full bg-redFF1 absolute -left-1 -top-1 animate-bounce"></span>
<img <img
onClick={() => setDrawerOpen((prevState) => !prevState)}
src={isDark ? hamburgerIcon : hamburgerBlueIcon} src={isDark ? hamburgerIcon : hamburgerBlueIcon}
alt="" alt=""
className="w-7 h-7.5" className="w-7 h-7.5"
/> />
</>
)}
{!title && back ? (
<img
src={isDark ? arrowWhiteIcon : arrowBlueIcon}
alt="back"
className={`w-6 ${isDark ? "transform rotate-180" : ""}`}
onClick={() => navigation(-1)}
/>
) : null}
{title && (
<strong className="text-blueC0 text-lg font-semibold dark:text-white">
{title}
</strong>
)}
</div> </div>
{logo && (
<Link <Link
to="/" to="/"
className="absolute left-1/2 transform -translate-y-1/2 top-1/2 -translate-x-1/2" className="absolute left-1/2 transform -translate-y-1/2 top-1/2 -translate-x-1/2"
> >
<img src={logoIcon} className="w-6 h-8" /> <img src={logoIcon} className="w-6 h-8" />
</Link> </Link>
)}
<div className="flex flex-row items-center"> <div className="flex flex-row items-center">
{qr && (
<>
<Link <Link
to="/qr" to="/qr"
className={_.join( className={_.join(
@ -120,15 +155,43 @@ function Navbar({ pages, isDark}) {
className={_.join(["w-6"], " ")} className={_.join(["w-6"], " ")}
/> />
</Link> </Link>
</>
)}
{bookmark && (
<img
src={
isDark ? bookmarkDeactiveDarkIcon : bookmarkDeactiveLightIcon
}
alt="bookmark"
className="w-7 ml-2"
/>
)}
{menu && (
<img
src={isDark ? dotsMenuDarkIcon : dotsMenuLightIcon}
alt="menu"
className="w-7"
/>
)}
{title && back ? (
<img
src={isDark ? arrowWhiteIcon : arrowBlueIcon}
alt="back"
className={`w-6 ${isDark ? "" : "transform -rotate-180"}`}
onClick={() => navigation(-1)}
/>
) : null}
</div> </div>
</div> </div>
){/* portrait end */} <Drawer drawerOpen={drawerOpen} setDrawerOpen={setDrawerOpen} />
{/* portrait end */}
</div> </div>
); );
} }
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
isDark: state.publicApi.isDark, isDark: state.publicApi.isDark,
headerOptions: state.publicApi.headerOptions,
}); });
export default connect(mapStateToProps, {})(Navbar); export default connect(mapStateToProps, {})(Navbar);
@ -160,4 +223,7 @@ Navbar.defaultProps = {
name: "وبلاگ", name: "وبلاگ",
}, },
], ],
logo: true,
hamburgerMenu: true,
qr: true,
}; };

@ -1,12 +1,14 @@
import proxy from "../proxy"; import proxy from "../proxy";
const publicApi = { const publicApi = {
setHeaderOptions: (data) => async (dispatch) =>
await dispatch({ type: "public/setHeaderOptions", data }),
setIsDark: (data) => async (dispatch) => setIsDark: (data) => async (dispatch) =>
await dispatch({ type: "public/setIsDark", data: data }), await dispatch({ type: "public/setIsDark", data }),
getHomeData: (data) => async (dispatch) => getHomeData: (data) => async (dispatch) =>
await proxy.get("public/homePage", data, { dispatch }), await proxy.get("public/homePage", data, { dispatch }),
setFaqActive: (data) => async (dispatch) => setFaqActive: (data) => async (dispatch) =>
await dispatch({ type: "public/faq/activate", data: data }), await dispatch({ type: "public/faq/activate", data }),
bookInfo: bookInfo:
(data = {}) => (data = {}) =>
async (dispatch) => async (dispatch) =>
@ -26,7 +28,7 @@ const publicApi = {
getBlogInfo: (data) => async (dispatch) => getBlogInfo: (data) => async (dispatch) =>
await proxy.get("public/blogInfo", data, { dispatch }), await proxy.get("public/blogInfo", data, { dispatch }),
setSubscribe: (data) => async (dispatch) => setSubscribe: (data) => async (dispatch) =>
await dispatch({ type: "public/setSubscribe", data: data }), await dispatch({ type: "public/setSubscribe", data }),
getProvince: getProvince:
(data = {}) => (data = {}) =>
async (dispatch) => async (dispatch) =>
@ -36,15 +38,15 @@ const publicApi = {
async (dispatch) => async (dispatch) =>
await proxy.get("public/city", data, { dispatch }), await proxy.get("public/city", data, { dispatch }),
selectMenu: (data) => async (dispatch) => selectMenu: (data) => async (dispatch) =>
await dispatch({ type: "public/selectMenu", data: data }), await dispatch({ type: "public/selectMenu", data }),
getHeader: getHeader:
(data = {}) => (data = {}) =>
async (dispatch) => async (dispatch) =>
await proxy.get("public/VOD/homePage", data, { dispatch }), await proxy.get("public/VOD/homePage", data, { dispatch }),
activeCategory: (data) => async (dispatch) => activeCategory: (data) => async (dispatch) =>
await dispatch({ type: "active/category", data: data }), await dispatch({ type: "active/category", data }),
setVideoActive: (data) => async (dispatch) => setVideoActive: (data) => async (dispatch) =>
await dispatch({ type: "active/video", data: data }), await dispatch({ type: "active/video", data }),
}; };
export default publicApi; export default publicApi;

@ -1,7 +1,7 @@
import location from "../../utils/location"; import location from "../../utils/location";
const initialState = { const initialState = {
isDark: localStorage.getItem("isDark") || false, isDark: localStorage.getItem("isDark") || true,
homeData: [], homeData: [],
newProducts: [], newProducts: [],
levels: [], levels: [],
@ -50,6 +50,15 @@ const initialState = {
11: "یازدهم", 11: "یازدهم",
12: "دوازدهم", 12: "دوازدهم",
}, },
headerOptions : {
shown : false,
logo : false,
hamburgerMenu :false,
qr : false,
menu : false,
back : false,
title : false,
}
}; };
const grades = [ const grades = [
@ -70,6 +79,8 @@ const grades = [
export default function publicApi(state = initialState, action) { export default function publicApi(state = initialState, action) {
let { type, data } = action; let { type, data } = action;
switch (type) { switch (type) {
case "public/setHeaderOptions":
return {...state, headerOptions : data};
case "public/setIsDark": case "public/setIsDark":
localStorage.setItem("isDark", data); localStorage.setItem("isDark", data);
return { ...state, location: false, error: null, isDark: data }; return { ...state, location: false, error: null, isDark: data };

@ -13,30 +13,96 @@ import Product from "./views/Product";
import ShoppingCart from "./views/ShoppingCart"; import ShoppingCart from "./views/ShoppingCart";
// import VideoTube from "./views/Home/VideoTube"; // import VideoTube from "./views/Home/VideoTube";
function Router({ isDark, isLogin }) { function Router({ isDark, isLogin, headerOptions }) {
return ( return (
<div className={_.join([isDark ? "dark" : ""], " ")}> <div className={_.join([isDark ? "dark" : ""], " ")}>
<div <div
className={_.join( className={_.join(
[ [
"min-h-screen min-w-screen rtl overflow-x-hidden py-20 lg:py-0", "min-h-screen min-w-screen rtl overflow-x-hidden pb-20 lg:py-0",
isDark ? "" : "bg-white", isDark ? "" : "bg-white",
headerOptions?.shown ? "pt-16" : "pt-4",
], ],
" " " "
)} )}
> >
<BrowserRouter> <BrowserRouter>
<Navbar /> {headerOptions?.shown && <Navbar headerOptions={headerOptions} />}
<BG /> <BG />
<BottomNavigation /> <BottomNavigation />
<Routes> <Routes>
<Route path="/" exact element={<Home />} /> <Route
path="/"
exact
element={
<Home
headerOptions={{
logo: true,
hamburgerMenu: true,
qr: true,
title: false,
back: false,
shown: true,
menu: false,
}}
/>
}
/>
<Route path="/login" exact element={<Auth page="login" />} /> <Route path="/login" exact element={<Auth page="login" />} />
<Route path="/signup" exact element={<Auth page="signup" />} /> <Route
path="/signup"
exact
element={
<Auth
page="signup"
headerOptions={{
logo: true,
hamburgerMenu: true,
qr: true,
title: false,
back: false,
shown: true,
menu: false,
}}
/>
}
/>
<Route path="/profile" exact element={<Auth page="profile" />} /> <Route path="/profile" exact element={<Auth page="profile" />} />
<Route path="/products" exact element={<Products />} /> <Route
<Route path="/cart" exact element={<ShoppingCart />} /> path="/products"
<Route path="/products/:id" exact element={<Product />} /> exact
element={<Products headerOptions={{ shown: false }} />}
/>
<Route
path="/cart"
exact
element={
<ShoppingCart
headerOptions={{
shown: true,
title: "سبد خرید",
back: true,
}}
/>
}
/>
<Route
path="/products/:id"
exact
element={
<Product
headerOptions={{
shown: true,
logo: false,
hamburgerMenu: false,
title: false,
back: true,
menu: true,
bookmark: true,
}}
/>
}
/>
<Route <Route
path="*" path="*"
element={ element={
@ -54,7 +120,8 @@ function Router({ isDark, isLogin }) {
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
isDark: state.publicApi.isDark, isDark: state.publicApi.isDark,
isLogin: state.user.status isLogin: state.user.status,
headerOptions: state.publicApi.headerOptions,
}); });
export default connect(mapStateToProps, {})(Router); export default connect(mapStateToProps, {})(Router);

@ -7,12 +7,19 @@ import onInput from "../../../utils/onInput";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { user } from "../../../redux/actions"; import { user, publicApi } from "../../../redux/actions";
import _ from "lodash"; import _ from "lodash";
//assets //assets
import logoIcon from "./assets/logo.svg"; import logoIcon from "./assets/logo.svg";
function SignUp({ sendOtp, sendPhoneNumber, loginFlag, theme }) { function SignUp({
sendOtp,
sendPhoneNumber,
loginFlag,
theme,
headerOptions,
setHeaderOptions,
}) {
const [level, setLevel] = useState("phonenumber"); const [level, setLevel] = useState("phonenumber");
const [signUpFields, setSignUpFiels] = useState({}); const [signUpFields, setSignUpFiels] = useState({});
const [error, setError] = useState(""); const [error, setError] = useState("");
@ -73,7 +80,12 @@ function SignUp({ sendOtp, sendPhoneNumber, loginFlag, theme }) {
}; };
const checkOtp = () => { const checkOtp = () => {
if (signUpFields?.otp1 && signUpFields?.otp2 && signUpFields?.otp3 && signUpFields?.otp4) { if (
signUpFields?.otp1 &&
signUpFields?.otp2 &&
signUpFields?.otp3 &&
signUpFields?.otp4
) {
sendOtp({ sendOtp({
cellphone: signUpFields?.cellphone, cellphone: signUpFields?.cellphone,
otp: otp:
@ -94,6 +106,7 @@ function SignUp({ sendOtp, sendPhoneNumber, loginFlag, theme }) {
useEffect(() => { useEffect(() => {
// numberInput.current.focus(); // numberInput.current.focus();
setHeaderOptions(headerOptions);
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -270,5 +283,6 @@ export default connect(
{ {
sendOtp: user.otp_login, sendOtp: user.otp_login,
sendPhoneNumber: user.otp, sendPhoneNumber: user.otp,
setHeaderOptions: publicApi.setHeaderOptions,
} }
)(SignUp); )(SignUp);

@ -1,24 +1,31 @@
import React, {useState, useEffect} from 'react'; import React, { useState, useEffect } from "react";
import SignUp from './SignUp/index'; import SignUp from "./SignUp/index";
import Profile from './Profile/index'; import Profile from "./Profile/index";
import Login from './Login/index'; import Login from "./Login/index";
import {connect} from 'react-redux'; import { connect } from "react-redux";
function Auth(props) { function Auth(props) {
const [page, setPage] = useState(props.page); const [page, setPage] = useState(props.page);
useEffect(() => { useEffect(() => {
setPage(() => props.page); setPage(() => props.page);
},[props.page]); }, [props.page]);
return ( return (
<> <>
{ page === 'signup' && <SignUp theme={props.theme} /> } {page === "signup" && (
{ page === 'login' && <Login theme={props.theme} setPage={setPage} /> } <SignUp theme={props.theme} headerOptions={props.headerOptions} />
{ page === 'profile' && <Profile theme={props.theme} /> } )}
{page === "login" && (
<Login
theme={props.theme}
setPage={setPage}
headerOptions={props.headerOptions}
/>
)}
{page === "profile" && <Profile theme={props.theme} />}
</> </>
) );
} }
export default connect((state) => ({ export default connect((state) => ({
theme : state.publicApi.theme, theme: state.publicApi.theme,
}))(Auth); }))(Auth);

@ -14,7 +14,7 @@ function Blogs({ list, getList, isDark }) {
<div className="w-full pr-4"> <div className="w-full pr-4">
<div className="w-full flex flex-col rounded-md py-4 overflow-hidden"> <div className="w-full flex flex-col rounded-md py-4 overflow-hidden">
<header className="w-full flex flex-row justify-between items-center"> <header className="w-full flex flex-row justify-between items-center">
<strong className="text-blueC0 dark:text-white font-bold text-lg"> <strong className="text-blueC0 dark:text-white font-bold text-sm">
خواندنیها خواندنیها
</strong> </strong>
<img <img

@ -11,7 +11,7 @@ function MyBooks({ list, getList, grades }) {
return ( return (
<div className="w-full px-4 mb-5"> <div className="w-full px-4 mb-5">
<div className="w-full flex flex-col rounded-md pt-4 px-2 bg-blueFF1 dark:bg-gray2077 overflow-hidden"> <div className="w-full flex flex-col rounded-md pt-4 px-2 bg-blueFF1 dark:bg-gray2077 overflow-hidden">
<strong className="text-blueC0 dark:text-white font-bold text-lg">کتابهای من</strong> <strong className="text-blueC0 dark:text-white font-bold text-sm">کتابهای من</strong>
<ul className="list-none w-full overflow-x-scroll pt-2 flex flex-row"> <ul className="list-none w-full overflow-x-scroll pt-2 flex flex-row">
{list.map((book, index) => ( {list.map((book, index) => (
<Book book={book} key={index} grades={grades} /> <Book book={book} key={index} grades={grades} />
@ -25,7 +25,7 @@ function MyBooks({ list, getList, grades }) {
const Book = ({ book, grades }) => { const Book = ({ book, grades }) => {
return ( return (
<li className="flex flex-col ml-3"> <li className="flex flex-col ml-3">
<Link to={"/products/" + book.name}> <Link to={"/products/" + book.id}>
<img <img
src={`https://dnvn.ir/api/v1/file/${book.fileIds}`} src={`https://dnvn.ir/api/v1/file/${book.fileIds}`}
alt="" alt=""

@ -14,7 +14,7 @@ function Videos({ list, getList, grades, isDark }) {
<div className="w-full px-4"> <div className="w-full px-4">
<div className="w-full flex flex-col rounded-md py-4 overflow-hidden"> <div className="w-full flex flex-col rounded-md py-4 overflow-hidden">
<header className="w-full flex flex-row justify-between items-center"> <header className="w-full flex flex-row justify-between items-center">
<strong className="text-blueC0 dark:text-white font-bold text-lg"> <strong className="text-blueC0 dark:text-white font-bold text-sm">
دورههای ویدئویی شما دورههای ویدئویی شما
</strong> </strong>
<img <img

@ -1,9 +1,8 @@
import React from "react"; import React, { useEffect } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import Public from "./Public"; import Public from "./Public";
import Private from "./Private"; import Private from "./Private";
function Portrait({ isLogin }) { function Portrait({ isLogin }) {
return ( return (
<div className="w-full min-h-screen lg:hidden z-10"> <div className="w-full min-h-screen lg:hidden z-10">
@ -16,4 +15,8 @@ const mapStateToProps = (state) => ({
isLogin: state.user.status, isLogin: state.user.status,
}); });
export default connect(mapStateToProps, {})(Portrait); const mapDispatchToProps = {
};
export default connect(mapStateToProps, mapDispatchToProps)(Portrait);

@ -1,13 +1,25 @@
import React from 'react'; import React, { useEffect } from "react";
import Landscape from './Landscape'; import { connect } from "react-redux";
import Portrait from './Portrait'; import Landscape from "./Landscape";
import Portrait from "./Portrait";
import { publicApi } from "../../redux/actions";
function Home({ setHeaderOptions, headerOptions }) {
export default function Home() { useEffect(() => {
setHeaderOptions(headerOptions);
}, []);
return ( return (
<> <>
<Portrait /> <Portrait />
<Landscape /> <Landscape />
</> </>
) );
} }
const mapStateToProps = (state) => ({});
const mapDispatchToProps = {
setHeaderOptions: publicApi.setHeaderOptions,
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);

@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { book, userProduct } from "../../redux/actions"; import { book, userProduct, publicApi } from "../../redux/actions";
import proxy from "../../redux/proxy"; import proxy from "../../redux/proxy";
import location from "../../utils/location"; import location from "../../utils/location";
import Button from "../../components/Button"; import Button from "../../components/Button";
@ -17,9 +17,14 @@ export const Product = ({
isDark, isDark,
userId, userId,
pushToCart, pushToCart,
headerOptions,
setHeaderOptions,
}) => { }) => {
const [type, setType] = useState("physical");
useEffect(() => { useEffect(() => {
getInfo({ id: location.getId() }); getInfo({ id: location.getId() });
setHeaderOptions(headerOptions);
}, []); }, []);
return ( return (
<> <>
@ -37,7 +42,9 @@ export const Product = ({
/> />
<div className="flex flex-1 flex-col pr-2 pt-2"> <div className="flex flex-1 flex-col pr-2 pt-2">
<h1 className="text-blue5F mb-3 font-semibold text-sm dark:text-white"> <h1 className="text-blue5F mb-3 font-semibold text-sm dark:text-white">
{book?.name} {type === "digital"
? book?.product[0]?.name
: book?.product[1]?.name}
</h1> </h1>
<div className="text-blueC0 text-xs dark:text-greenBA"> <div className="text-blueC0 text-xs dark:text-greenBA">
{"پایه" + " " + grades[book?.gradeId]} {"پایه" + " " + grades[book?.gradeId]}
@ -58,7 +65,7 @@ export const Product = ({
{book?.rate} <br /> امتیاز {book?.rate} <br /> امتیاز
</li> </li>
</ul> </ul>
<h2 className="text-blueC0 text-sm font-light mt-5 dark:text-greenBA"> <h2 className="text-blueC0 text-sm font-semibold mt-7 dark:text-greenBA">
نسخه محصول را انتخاب کنید نسخه محصول را انتخاب کنید
</h2> </h2>
<div className="w-full flex flex-row justify-between items-center mt-4"> <div className="w-full flex flex-row justify-between items-center mt-4">
@ -68,13 +75,19 @@ export const Product = ({
border={{ color: isDark ? "#ffffff55" : "#196EC055", width: "1px" }} border={{ color: isDark ? "#ffffff55" : "#196EC055", width: "1px" }}
width="49%" width="49%"
color={isDark ? "text-white" : "text-blueC0"} color={isDark ? "text-white" : "text-blueC0"}
active={type === "physical"}
selectable={true}
onClick={() => setType("physical")}
/> />
<Button <Button
title="نسخه دیجیتال" title="نسخه دیجیتال"
backgroundColor={isDark ? "bg-transparent" : "bg-white"} backgroundColor={isDark ? "bg-transparent" : "bg-white"}
border={{ color: isDark ? "#ffffff55" : "#196EC055", width: "1px" }} border={{ color: isDark ? "#ffffff55" : "#196EC055", width: "1px" }}
width="49%" width="49%"
selectable={true}
active={type === "digital"}
color={isDark ? "text-white" : "text-blueC0"} color={isDark ? "text-white" : "text-blueC0"}
onClick={() => setType("digital")}
/> />
</div> </div>
<div className="w-full flex flex-row justify-between items-center mt-4"> <div className="w-full flex flex-row justify-between items-center mt-4">
@ -82,13 +95,24 @@ export const Product = ({
onClick={() => onClick={() =>
proxy.status() proxy.status()
? pushToCart({ ? pushToCart({
productId: book.product[1].id, productId:
type === "digital"
? book?.product[0]?.id
: book?.product[1]?.id,
userId: userId, userId: userId,
count: 1, count: 1,
}) })
: toast.info("ابتدا وارد حساب کاربری خود شوید.") : toast.info("ابتدا وارد حساب کاربری خود شوید.")
} }
title="خرید با قیمت 245.000 تومان" title={
"خرید با قیمت" +
" " +
(type === "digital"
? book?.product[0]?.price
: book?.product[1]?.price) +
" " +
"تومان"
}
backgroundColor="bg-blueC0" backgroundColor="bg-blueC0"
border={{ color: "#196EC055", width: "1px" }} border={{ color: "#196EC055", width: "1px" }}
width="65%" width="65%"
@ -149,7 +173,11 @@ const mapStateToProps = (state) => ({
userId: state.user.status.id, userId: state.user.status.id,
}); });
const mapDispatchToProps = { getInfo: book.info, pushToCart: userProduct.add }; const mapDispatchToProps = {
getInfo: book.info,
pushToCart: userProduct.add,
setHeaderOptions: publicApi.setHeaderOptions,
};
export default connect(mapStateToProps, mapDispatchToProps)(Product); export default connect(mapStateToProps, mapDispatchToProps)(Product);

@ -3,7 +3,7 @@ import { Link } from "react-router-dom";
import { connect } from "react-redux"; import { connect } from "react-redux";
import Search from "../../components/Search"; import Search from "../../components/Search";
import Select from "../../components/Select"; import Select from "../../components/Select";
import { book } from "../../redux/actions"; import { book, publicApi } from "../../redux/actions";
//assets //assets
import searchLight from "../../assets/icons/search-light.svg"; import searchLight from "../../assets/icons/search-light.svg";
@ -65,6 +65,8 @@ export const Products = ({
gradeFilter, gradeFilter,
selectedGrade, selectedGrade,
gradeFilterBooks, gradeFilterBooks,
setHeaderOptions,
headerOptions,
}) => { }) => {
const [filter, setFilter] = useState({ const [filter, setFilter] = useState({
search: "", search: "",
@ -78,6 +80,7 @@ export const Products = ({
useEffect(() => { useEffect(() => {
getList(); getList();
setHeaderOptions(headerOptions);
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -154,6 +157,7 @@ const mapStateToProps = (state) => ({
const mapDispatchToProps = { const mapDispatchToProps = {
getList: book.list, getList: book.list,
gradeFilter: book.gradeFilter, gradeFilter: book.gradeFilter,
setHeaderOptions: publicApi.setHeaderOptions,
}; };
export default connect(mapStateToProps, mapDispatchToProps)(Products); export default connect(mapStateToProps, mapDispatchToProps)(Products);

@ -1,14 +1,37 @@
import React from "react"; import React, {useState} from "react";
import { connect } from "react-redux";
import { userFactor } from "../../../redux/actions";
function Code({ codeId, setCodeId, userId, factorId }) {
const [value, setValue] = React.useState(codeId || "");
const setOff = () => {
setCodeId({ offCodeValue: value, userId: userId, id: factorId });
}
export default function Code({}) {
return ( return (
<form className="flex w-full flex-row border border-blueC0 border-solid rounded-md mt-3 overflow-hidden"> <form className="flex w-full flex-row border border-blueC0 border-solid rounded-md mt-3 overflow-hidden">
<input <input
type="text" type="text"
className="border-0 flex-1 text-right pr-3 font-medium text-xs dark:bg-gray2077 placeholder:text-blue5F" className="border-0 flex-1 text-right pr-3 font-medium text-xs dark:bg-gray2077 placeholder:text-blue5F dark:text-white"
placeholder={"کد تخفیف دارید ؟"} placeholder={"کد تخفیف دارید ؟"}
/> />
<button className={"py-3 px-4 text-xs bg-blueC0 text-white border border-blueC0 border-solid"}>ثبت کد</button> <button className={"py-3 px-4 text-xs bg-blueC0 text-white border border-blueC0 border-solid"} onClick={setOff}>ثبت کد</button>
</form> </form>
); );
} }
const mapStateToProps = (state) => ({
codeId: state.userFactor.info?.codeId,
userId: state.user.status.id,
factorId: state.userFactor.info?.id,
});
const mapDispatchToProps = {
setCodeId: userFactor.update,
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Code);

@ -1,43 +1,95 @@
import React from "react"; import React from "react";
import { connect } from "react-redux";
import { userProduct } from "../../../redux/actions";
// assets // assets
import bookImage from "../../../assets/images/zist-11.svg"; import bookImage from "../../../assets/images/zist-11.svg";
import exitIcon from "../../../assets/icons/exit.svg"; import exitIcon from "../../../assets/icons/exit.svg";
export default function Product({ product }) { function Product({ product, grades, pushToCart }) {
return ( return (
<div className="flex flex-row justify-between py-5 border-b border-solid border-grayDB dark:border-gray45"> <div className="flex flex-row justify-between py-5 border-b border-solid border-grayDB dark:border-gray45">
<div className="flex flex-row"> <div className="flex flex-row">
<img src={bookImage} className="rounded-md w-20" /> <img
src={"https://dnvn.ir/api/v1/file/" + product.product.book.fileIds}
className="rounded-md w-20"
/>
<div className="flex flex-col pr-2"> <div className="flex flex-col pr-2">
<strong className="text-sm text-blue5F mb-1 dark:text-white"> <strong className="text-sm text-blue5F mb-1 dark:text-white">
علوم اجتماعی {product.product.book.name}
</strong> </strong>
<span className="text-gray70 text-xs mb-1 dark:text-greenBA"> <span className="text-gray70 text-xs mb-1 dark:text-greenBA">
پایه دوم {"پایه" + " " + grades[product.product.book.gradeId]}
</span> </span>
<span className="text-gray70 text-xs mb-1 dark:text-greenBA"> <span className="text-gray70 text-xs mb-1 dark:text-greenBA">
نسخه فیزیکی {product.productType === 2 ? "نسخه فیزیکی" : "نسخه دیجیتال"}
</span> </span>
</div> </div>
</div> </div>
<div className={"flex flex-col justify-between items-end"}> <div className={"flex flex-col justify-between items-end"}>
<img src={exitIcon} className="w-4" alt="" /> <img
src={exitIcon}
className="w-4"
alt=""
onClick={() =>
pushToCart(
{
productId: product?.productId,
userId: product?.userId,
count: product?.productType === 1 ? -1 : -product?.count,
},
{},
{id : product?.factorId}
)
}
/>
<div className="flex flex-col items-center"> <div className="flex flex-col items-center">
{product.productType === 2 && (
<div className="flex flex-row items-center mb-3"> <div className="flex flex-row items-center mb-3">
<button className="rounded-md pt-2 pb-1.5 px-3 border border-solid border-blueC0 text-blueC0"> <button
className="rounded-md pt-2 pb-1.5 px-3 border border-solid border-blueC0 text-blueC0"
onClick={() =>
pushToCart({
productId: product?.productId,
userId: product?.userId,
count: 1,
})
}
>
+ +
</button> </button>
<span className="mx-2.5 font-semibold text-md text-blueC0">1</span> <span className="mx-2.5 font-semibold text-md text-blueC0">
<button className="rounded-md pt-2 pb-1.5 px-3 border border-solid border-grayDB text-grayDB dark:border-blueC0 dark:text-blueC0"> {product.count}
</span>
<button
className="rounded-md pt-2 pb-1.5 px-3 border border-solid border-grayDB text-grayDB dark:border-blueC0 dark:text-blueC0"
onClick={() =>
pushToCart({
productId: product?.productId,
userId: product?.userId,
count: -1,
})
}
>
- -
</button> </button>
</div> </div>
)}
<strong className="font-bold text-md text-blue5F dark:text-white"> <strong className="font-bold text-md text-blue5F dark:text-white">
145.000 {product.product.price}&nbsp;تومان
</strong> </strong>
</div> </div>
</div> </div>
</div> </div>
); );
} }
const mapStateToProps = (state) => ({
grades: state.publicApi.grades,
});
const mapDispatchToProps = {
pushToCart: userProduct.add,
};
export default connect(mapStateToProps, mapDispatchToProps)(Product);

@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { userProduct, userFactor } from "../../redux/actions"; import { userProduct, userFactor, publicApi } from "../../redux/actions";
import Product from "./Product"; import Product from "./Product";
import Code from "./Code"; import Code from "./Code";
@ -15,12 +15,12 @@ const PriceStatus = ({ name, value, type, isDark }) => {
<div className="w-full flex flex-row justify-between py-2"> <div className="w-full flex flex-row justify-between py-2">
<strong className="font-light text-sm dark:text-white">{name}</strong> <strong className="font-light text-sm dark:text-white">{name}</strong>
<span <span
className="font-normal text-xs dark:text-white" className="font-bold text-xs dark:text-white"
style={{ style={{
color: !isDark ? (type === "error" ? "#F1420D" : "#246E8A") : "", color: !isDark ? (type === "error" ? "#F1420D" : "#246E8A") : "",
}} }}
> >
{value} {value + " " + "تومان"}
</span> </span>
</div> </div>
); );
@ -36,33 +36,37 @@ function ShoppingCart({
getList, getList,
getInfo, getInfo,
getFactorInfo, getFactorInfo,
payFactor payFactor,
headerOptions,
setHeaderOptions,
}) { }) {
useEffect(() => { useEffect(() => {
getList();
},[]); getFactorInfo({ userId });
setHeaderOptions(headerOptions);
}, []);
return ( return (
<div className="w-full flex flex-col px-4"> <div className="w-full flex flex-col px-4">
{[0, 1].map((product, index) => ( {list.map((product, index) => (
<Product key={index} product={product} /> <Product key={index} product={product} />
))} ))}
<Code /> <Code />
<div className="flex flex-col w-full border-t py-2 border-gray-300 border-b mt-6"> <div className="flex flex-col w-full border-t py-2 border-gray-300 border-b mt-6">
<PriceStatus <PriceStatus
name="مبلغ سبد خرید" name="مبلغ سبد خرید"
value="265000 تومان" value={factorInfo?.sumPrice}
type="normal" type="normal"
isDark={isDark} isDark={isDark}
/> />
<PriceStatus <PriceStatus
name="میزان تخفیف" name="میزان تخفیف"
value="0 تومان" value={factorInfo?.offPrice}
type="error" type="error"
isDark={isDark} isDark={isDark}
/> />
<PriceStatus <PriceStatus
name="مالیات بر ارزش افزوده" name="مالیات بر ارزش افزوده"
value="0 تومان" value={factorInfo?.sumPrice * 0.009}
type="error" type="error"
isDark={isDark} isDark={isDark}
/> />
@ -72,7 +76,7 @@ function ShoppingCart({
جمع کل سبد خرید جمع کل سبد خرید
</strong> </strong>
<p className="font-medium text-greenBA text-xs dark:text-white"> <p className="font-medium text-greenBA text-xs dark:text-white">
235.000 تومان {factorInfo?.payPrice + " " + "تومان"}
</p> </p>
</div> </div>
<div <div
@ -90,6 +94,7 @@ function ShoppingCart({
border={{ color: "#196EC055", width: "1px" }} border={{ color: "#196EC055", width: "1px" }}
width="100%" width="100%"
color="text-white" color="text-white"
// onClick={() => }
/> />
</div> </div>
); );
@ -109,6 +114,7 @@ const mapDispatchToProps = {
getInfo: userProduct.info, getInfo: userProduct.info,
getFactorInfo: userFactor.info, getFactorInfo: userFactor.info,
payFactor: userFactor.payment, payFactor: userFactor.payment,
setHeaderOptions: publicApi.setHeaderOptions,
}; };
export default connect(mapStateToProps, mapDispatchToProps)(ShoppingCart); export default connect(mapStateToProps, mapDispatchToProps)(ShoppingCart);

@ -9422,6 +9422,11 @@ react-is@^17.0.1, react-is@^17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-modern-drawer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/react-modern-drawer/-/react-modern-drawer-1.0.1.tgz#a400c551216b3dffadf93e80f7cc313d30434d13"
integrity sha512-Stswd8ETIRTGLJl7aFtu92kXb+7UCPn2mzTvpGWsE1kMXco3dK+PZyw/4NGGwDpesZvwPsz3PFlO1BuVqBZpCg==
react-redux@^7.2.6: react-redux@^7.2.6:
version "7.2.6" version "7.2.6"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.6.tgz#49633a24fe552b5f9caf58feb8a138936ddfe9aa"

Loading…
Cancel
Save