reza added something

temp
Reza_ashrafi 3 years ago
parent 133361b9e0
commit 32a2a7e0c9
  1. 9
      src/components/Button/index.js
  2. 49
      src/components/Dialog/index.js
  3. 17
      src/components/Input/index.js
  4. 43
      src/components/Loading/index.js
  5. 12
      src/components/Loading/index.scss
  6. 2
      src/components/ProductStatusCard/index.js
  7. 2
      src/components/Radio/index.js
  8. 49
      src/components/ReverseMap/index.js
  9. 1
      src/components/Select/index.js
  10. 18
      src/components/VodProduct/index.js
  11. 4
      src/views/404/index.js
  12. 227
      src/views/Address/index.js
  13. 33
      src/views/DeliveryForm/Address/index.js
  14. 6
      src/views/DeliveryForm/TransportMethod/index.js
  15. 2
      src/views/Home/Desktop/index.js
  16. 4
      src/views/Product/Desktop/ProductOverAll/index.js
  17. 2
      src/views/Products/index.js
  18. 60
      src/views/ShoppingCart/Product/index.js
  19. 46
      src/views/ShoppingCart/index.js
  20. 2
      src/views/Video/index.js
  21. 86
      tailwind.config.js

@ -1,4 +1,5 @@
import React from "react"; import React from "react";
import Loading from "../Loading";
export default function Button({ export default function Button({
title, title,
@ -11,13 +12,11 @@ export default function Button({
selectable, selectable,
active, active,
borderType, borderType,
twPaddings, loading,
customStyle,
radius
}) { }) {
return ( return (
<button <button
className={`${radius == false ? '' : 'rounded-md'} ${twPaddings? twPaddings : 'py-3'} ${customStyle} justify-center text-sm lg:text-tiny cursor-pointer lg:py-4 ${ className={`rounded-md flex py-3 justify-center text-sm lg:text-tiny cursor-pointer lg:py-4 ${
selectable ? (active ? "bg-black" : backgroundColor) : backgroundColor selectable ? (active ? "bg-black" : backgroundColor) : backgroundColor
} ${selectable ? (active ? "text-blueC0" : color) : color} ${ } ${selectable ? (active ? "text-blueC0" : color) : color} ${
selectable ? (active ? "bg-blue-100" : bgOpacity) : bgOpacity selectable ? (active ? "bg-blue-100" : bgOpacity) : bgOpacity
@ -32,7 +31,7 @@ export default function Button({
}} }}
onClick={() => onClick()} onClick={() => onClick()}
> >
{title} {loading ? <Loading size={2} color="bg-green-500" /> : title}
</button> </button>
); );
} }

@ -0,0 +1,49 @@
import React from "react";
import { connect } from "react-redux";
export const Dialog = ({ dialog }) => {
const { text, type, setOpen, accept, bg, color } = dialog;
return (
<div
className="z-50 w-screen h-full fixed top-0 right-0 backdrop-filter blur-sm flex justify-center items-center"
onClick={() => setOpen(false)}
>
<div className={`flex flex-col items-center ${bg} backdrop-filter backdrop-blur-xl rounded-md p-5 shadow-2xl`} style={{minWidth: 300}}>
{/* {type === "confirm" && (
<div className="w-full flex justify-end text-sm text-bla">exit</div>
)} */}
<p className={`text-tiny ${color} w-full mb-8 leading-7`}>{text}</p>
{type === "confirm" && (
<div className="flex flex-row gap-4 w-full">
<button
className="py-2.5 w-1/2 shadow-md rounded-sm bg-blueC0 text-white"
onClick={() => accept()}
>
باشه
</button>
<button
className="py-2.5 w-1/2 shadow-md rounded-sm bg-redFF1 text-white"
onClick={() => setOpen(false)}
>
بیخیال
</button>
</div>
)}
{type === "alert" && (
<div className="flex flex-row gap-4 w-full">
<button
className="py-2.5 w-full shadow-md rounded-sm bg-blueC0 text-white"
onClick={() => setOpen(false)}
>
باشه
</button>
</div>
)}
</div>
</div>
);
};
const mapStateToProps = (state) => ({});
export default connect(mapStateToProps)(Dialog);

@ -19,14 +19,13 @@ const inputStatusHandler = (value, focus, status, primary, success, danger) => {
if (status) { if (status) {
return success; return success;
} else { } else {
return danger; return;
} }
} }
} }
}; };
export default function Input(props) { export default function Input(props) {
const [value, setValue] = useState("");
const [focusToggle, setFocusToggle] = useState(false); const [focusToggle, setFocusToggle] = useState(false);
const { const {
@ -44,6 +43,7 @@ export default function Input(props) {
regex, regex,
direction, direction,
width, width,
value,
} = props; } = props;
return ( return (
@ -93,6 +93,7 @@ export default function Input(props) {
], ],
" " " "
)} )}
value={value}
style={{ direction: direction }} style={{ direction: direction }}
onChange={(e) => onChange={(e) =>
onChange(name, (e.target.value = regex(e.target.value))) onChange(name, (e.target.value = regex(e.target.value)))
@ -101,7 +102,7 @@ export default function Input(props) {
onBlur={() => (!value ? setFocusToggle(false) : setFocusToggle(true))} onBlur={() => (!value ? setFocusToggle(false) : setFocusToggle(true))}
maxLength={maxLength} maxLength={maxLength}
/> />
{!status && value && ( {/* {!status && value && (
<img <img
src={danger} src={danger}
alt={langDetector(lang, "اشتباه", "wrong")} alt={langDetector(lang, "اشتباه", "wrong")}
@ -113,8 +114,8 @@ export default function Input(props) {
" " " "
)} )}
/> />
)} )} */}
{status && value && ( {/* {status && value && (
<img <img
src={tick} src={tick}
alt={langDetector(lang, "درست", "right")} alt={langDetector(lang, "درست", "right")}
@ -126,7 +127,7 @@ export default function Input(props) {
" " " "
)} )}
/> />
)} )} */}
{value && !status && ( {value && !status && (
<span <span
@ -150,7 +151,7 @@ export default function Input(props) {
{message} {message}
</span> </span>
)} )}
{value && ( {/* {value && (
<span <span
className={_.join( className={_.join(
[ [
@ -171,7 +172,7 @@ export default function Input(props) {
> >
{value.length + "/" + maxLength} {value.length + "/" + maxLength}
</span> </span>
)} )} */}
</div> </div>
); );
} }

@ -0,0 +1,43 @@
import React, { useState, useEffect } from "react";
import "./index.scss";
function Loading({ size, color }) {
const [dots, setDots] = useState({});
useEffect(() => {
setTimeout(() => {
setDots({ ...dots, one: true });
}, 50);
setTimeout(() => {
setDots({ ...dots, one : true, two: true });
}, 500);
setTimeout(() => {
setDots({ ...dots, one : true, two: true, three: true });
}, 950);
}, []);
return (
<div className="flex items-center gap-0.5">
<span
className={`w-${size} h-${size} rounded-full ${color} ${
dots?.one ? "loading-animation" : ""
}`}
></span>
<span
className={`w-${size} h-${size} rounded-full ${color} ${
dots?.two ? "loading-animation" : ""
}`}
></span>
<span
className={`w-${size} h-${size} rounded-full ${color} ${
dots?.three ? "loading-animation" : ""
}`}
></span>
</div>
);
}
export default Loading;
Loading.defaultProps = {
size: 2,
};

@ -0,0 +1,12 @@
.loading-animation{
animation: loading 0.6s infinite;
}
@keyframes loading{
0%{
opacity: 0.5;
}
100%{
opacity: 1;
}
}

@ -22,6 +22,7 @@ const ProductStatusCard = ({
userId, userId,
isLogin, isLogin,
pushToCart, pushToCart,
loading,
}) => { }) => {
return ( return (
<section className="w-full"> <section className="w-full">
@ -69,6 +70,7 @@ const ProductStatusCard = ({
width="100%" width="100%"
color={"text-white"} color={"text-white"}
selectable={true} selectable={true}
loading={loading}
onClick={() => onClick={() =>
isLogin isLogin
? pushToCart({ ? pushToCart({

@ -4,7 +4,7 @@ import "./index.scss";
export default function Radio(props) { export default function Radio(props) {
return ( return (
<div className="radio flex items-center"> <div className="radio flex items-center">
<input id="radio-1" name={props.name} type="radio" /> <input id="radio-1" name={props.name} type="radio" checked={props.active} />
<label htmlFor="radio-1" className="radio-label text-xs"> <label htmlFor="radio-1" className="radio-label text-xs">
{/* {props.label} */} {/* {props.label} */}
</label> </label>

@ -0,0 +1,49 @@
import React, { useState } from "react";
import Mapir from "mapir-react-component";
const X_API_KEY = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6IjkyODE4ODI0N2FhMjdlYTMyMzVlYzRiOWUxZDI3NTQyZTNjYmI3ZjQzY2Q2NGQzYjdkYjU1NmY3NDM5YzU2NzUyNzMxMjAzYzMwOTliYTFiIn0.eyJhdWQiOiIxNjk2MSIsImp0aSI6IjkyODE4ODI0N2FhMjdlYTMyMzVlYzRiOWUxZDI3NTQyZTNjYmI3ZjQzY2Q2NGQzYjdkYjU1NmY3NDM5YzU2NzUyNzMxMjAzYzMwOTliYTFiIiwiaWF0IjoxNjQ0MzE3MDA5LCJuYmYiOjE2NDQzMTcwMDksImV4cCI6MTY0NjgyMjYwOSwic3ViIjoiIiwic2NvcGVzIjpbImJhc2ljIl19.Gckmd77Vw-wmsaztsmVmjYmfp4zXRSy2f0tXS-1p7IY8VJmvCCrk64y7WvCLbH0YfGZI9rG9ZUi5hRJDDGXu70l1ZafCYq2-EkMzcbx-PGTTq2bHGNZujF4ur1xtGoXd2kwGuldiR_6SJdqYLHcupbscFI7hHaQnkN1xWJ-L8476ZOKzUgiBvwH9uElGZXwD6SwfXoGW_R38_isrnfTXKfW9PPixyLaN3404tcjOU1r02HIurrplXlIKRKG9CVXxKsESfJJ8trwtZw6O4erxrnisxfL0VwAR7l69P6KcFb57vvM4j4hKFqxUDr4YvSq1qOTt3urYbAKehhvoiLhZxw";
const Map = Mapir.setToken({
transformRequest: (url) => {
return {
url: url,
headers: {
"x-api-key":X_API_KEY,
"Mapir-SDK": "reactjs",
},
};
},
});
const ReverseMap = ({ setAddress }) => {
const [markerArray, setMarkerArray] = useState([]);
const [coord, setCoord] = useState([51.42, 35.72]);
function reverseFunction(map, e) {
let url = `https://map.ir/reverse/no?lat=${e.lngLat.lat}&lon=${e.lngLat.lng}`;
fetch(url, {
headers: {
"Content-Type": "application/json",
"x-api-key": X_API_KEY,
},
})
.then((response) => response.json())
.then((data) => setAddress(data.address));
const array = [];
array.push(
<Mapir.Marker
coordinates={[e.lngLat.lng, e.lngLat.lat]}
anchor="bottom"
/>
);
setMarkerArray(array);
}
return (
<Mapir center={coord} Map={Map} onClick={reverseFunction}>
{markerArray}
</Mapir>
);
};
export default ReverseMap;

@ -41,4 +41,5 @@ export default Select;
Select.defaultProps = { Select.defaultProps = {
options: ["Really", "Option 2", "Option 3"], options: ["Really", "Option 2", "Option 3"],
borderColor : 'border-gray-300'
}; };

@ -1,6 +1,6 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import {Link} from 'react-router-dom'; import { Link } from "react-router-dom";
import separate from '../../utils/separate'; import separate from "../../utils/separate";
import StarRating from "../StarRating"; import StarRating from "../StarRating";
import ReactTooltip from "react-tooltip"; import ReactTooltip from "react-tooltip";
@ -68,7 +68,7 @@ const VodProduct = ({
<> <>
{/* product UI Design */} {/* product UI Design */}
<Link <Link
to={'/videos/' + video?.bookId} to={"/videos/" + video?.id}
className="flex flex-wrap sm:flex-nowrap justify-around" className="flex flex-wrap sm:flex-nowrap justify-around"
style={{ direction: "rtl" }} style={{ direction: "rtl" }}
> >
@ -146,7 +146,7 @@ const VodProduct = ({
</h2> </h2>
{console.log(video?.gradeId)} {console.log(video?.gradeId)}
<span className={`text-sm font-light text-blueC0`}> <span className={`text-sm font-light text-blueC0`}>
{ 'پایه' + ' ' + grades[video?.gradeId]} {"پایه" + " " + grades[video?.gradeId]}
</span> </span>
</div> </div>
{productContent && ( {productContent && (
@ -175,9 +175,7 @@ const VodProduct = ({
: "flex" : "flex"
}`} }`}
> >
<span className="text-xs text-borderColor ml-2"> <span className="text-xs text-borderColor ml-2"></span>
</span>
{/* {discountPrice && ( {/* {discountPrice && (
<p className="line-through text-xs text-borderColor"> <p className="line-through text-xs text-borderColor">
@ -185,12 +183,12 @@ const VodProduct = ({
</p> </p>
)} */} )} */}
</div> </div>
{/* <p <p
className={`text-base font-semibold text-left`} className={`text-base font-semibold text-left`}
> >
{separate(video?.price) + ' '} {separate(video?.product[2]?.price) + ' '}
<span>تومان</span> <span>تومان</span>
</p> */} </p>
</div> </div>
{/* add to cart */} {/* add to cart */}
{addToCartHolder && ( {addToCartHolder && (

@ -19,9 +19,9 @@ function NotFound({ headerOptions, setHeaderOptions }) {
</h1> </h1>
<img src={notFoundImage} alt="404" className="mt-10" /> <img src={notFoundImage} alt="404" className="mt-10" />
<p className="text-base dark:text-white mt-12 leading-7"> <p className="text-base dark:text-white mt-12 leading-7">
وای!! اوه!!
<br /> <br />
صفحه ای که بدنبال آن بودهاید دیگر وجود ندارد :( دوست من، اشتباه اومدی
</p> </p>
</div> </div>
); );

@ -1,12 +1,37 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { publicApi } from "../../redux/actions"; import { publicApi, userAddress } from "../../redux/actions";
import onInput from "../../utils/onInput"; import onInput from "../../utils/onInput";
import Input from "../../components/Input"; import Input from "../../components/Input";
import Select from "../../components/Select"; import Select from "../../components/Select";
import Button from "../../components/Button";
import ReverseMap from "../../components/ReverseMap";
import Dialog from "../../components/Dialog";
export const Address = ({ city, province, getCity, getProvince }) => { //assets
import dropdownIcon from "../../assets/icons/dropdown-blue.svg";
export const Address = ({
city,
province,
getCity,
getProvince,
provinces,
cities,
createAddress,
loading,
setPhase,
}) => {
const [addressFields, setAddressFields] = useState({}); const [addressFields, setAddressFields] = useState({});
const [mapAddress, setMapAddress] = useState(null);
const [dialog, setDialog] = useState({
text: null,
type: null,
setOpen: null,
accept: null,
color: null,
open: null,
});
useEffect(() => { useEffect(() => {
if (province.length <= 1) { if (province.length <= 1) {
@ -16,11 +41,144 @@ export const Address = ({ city, province, getCity, getProvince }) => {
useEffect(() => { useEffect(() => {
if (addressFields?.provinceId) { if (addressFields?.provinceId) {
getCity({ provinceId: addressFields?.provinceId }); getCity({
provinceId: provinces.filter(
(province) => province.name === addressFields?.provinceId
)[0].id,
});
} }
}, [addressFields]); }, [addressFields]);
const onChange = (name, value) => {}; const onChange = (name, value) => {
console.log("1");
setAddressFields({ ...addressFields, [name]: value });
};
const submit = () => {
const {
firstName,
lastName,
cellphone,
phone,
address,
provinceId,
cityId,
postalCode,
} = addressFields;
if (!firstName) {
setDialog({
text: "نام خود را وارد کنید.",
type: "alert",
setOpen: (val) => setDialog({ ...dialog, open: val }),
accept: () => {},
bg: "bg-black bg-opacity-60",
color: "text-white",
open: true,
});
return;
}
if (!lastName) {
setDialog({
text: "نام خانوادگی خود را وارد کنید.",
type: "alert",
setOpen: (val) => setDialog({ ...dialog, open: val }),
accept: () => {},
bg: "bg-black bg-opacity-60",
color: "text-white",
open: true,
});
return;
}
if (!cellphone) {
setDialog({
text: "شماره موبایل خود را وارد کنید.",
type: "alert",
setOpen: (val) => setDialog({ ...dialog, open: val }),
accept: () => {},
bg: "bg-black bg-opacity-60",
color: "text-white",
open: true,
});
return;
}
if (!phone) {
setDialog({
text: "شماره تلفن خود را وارد کنید.",
type: "alert",
setOpen: (val) => setDialog({ ...dialog, open: val }),
accept: () => {},
bg: "bg-black bg-opacity-60",
color: "text-white",
open: true,
});
return;
}
if (!provinceId) {
setDialog({
text: "نام استان خود را وارد کنید.",
type: "alert",
setOpen: (val) => setDialog({ ...dialog, open: val }),
accept: () => {},
bg: "bg-black bg-opacity-60",
color: "text-white",
open: true,
});
return;
}
if (!cityId) {
setDialog({
text: "نام شهرستان خود را وارد کنید.",
type: "alert",
setOpen: (val) => setDialog({ ...dialog, open: val }),
accept: () => {},
bg: "bg-black bg-opacity-60",
color: "text-white",
open: true,
});
return;
}
if (!postalCode) {
setDialog({
text: "کد پستی خود را وارد کنید.",
type: "alert",
setOpen: (val) => setDialog({ ...dialog, open: val }),
accept: () => {},
bg: "bg-black bg-opacity-60",
color: "text-white",
open: true,
});
return;
}
if (!mapAddress) {
setDialog({
text: "آدرس خود را از روی نقشه انتخاب کن",
type: "alert",
setOpen: (val) => setDialog({ ...dialog, open: val }),
accept: () => {},
bg: "bg-black bg-opacity-60",
color: "text-white",
open: true,
});
return;
}
createAddress({
firstName,
lastName,
cellphone,
phone,
address: mapAddress,
provinceId: provinces.filter(
(province) => province.name === provinceId
)[0].id,
cityId: cities.filter((city) => city.name === cityId)[0]?.id,
postalCode,
location: null,
});
setPhase("deliveryForm");
};
const Header = ({ title, text }) => { const Header = ({ title, text }) => {
return ( return (
@ -33,9 +191,26 @@ export const Address = ({ city, province, getCity, getProvince }) => {
return ( return (
<div <div
className="flex flex-row-reverse flex-1 pb-20 gap-5 divide-x divide-solid divide-gray-200" className="flex flex-row-reverse flex-1 pb-20 gap-5 divide-x divide-solid divide-gray-200"
style={{ minHeight: "calc(100vh - 500px)" }} // style={{ minHeight: "calc(100vh - 500px)" }}
> >
<div className="flex flex-col w-1/2"></div> <div className="flex flex-col w-1/2 px-4">
<div
className="w-full overflow-hidden rounded-md mb-6"
style={{ height: "calc(100vh / 2)" }}
>
<ReverseMap setAddress={(val) => setMapAddress(val)} />
</div>
<Input
width="100%"
label={"ادرس انتخاب شده در نقشه"}
regex={(val) => onInput.numberOnly(val)}
name="address"
onChange={(name, value) => setMapAddress(value)}
value={mapAddress}
align="text-right"
direction="rtl"
/>
</div>
<div className="flex flex-col w-1/2 pl-4"> <div className="flex flex-col w-1/2 pl-4">
<Header <Header
title="اطلاعات هویتی" title="اطلاعات هویتی"
@ -80,16 +255,18 @@ export const Address = ({ city, province, getCity, getProvince }) => {
value={addressFields?.cellphone} value={addressFields?.cellphone}
align="text-right" align="text-right"
direction="ltr" direction="ltr"
maxLength={11}
/> />
<Input <Input
width="50%" width="50%"
label={"شماره تلفن"} label={"شماره تلفن"}
regex={(val) => onInput.numberOnly(val)} regex={(val) => onInput.numberOnly(val)}
name="tellphone" name="phone"
onChange={(name, value) => onChange(name, value)} onChange={(name, value) => onChange(name, value)}
value={addressFields?.lastName} value={addressFields?.phone}
align="text-right" align="text-right"
direction="ltr" direction="ltr"
maxLength={11}
/> />
</div> </div>
<br /> <br />
@ -116,13 +293,39 @@ export const Address = ({ city, province, getCity, getProvince }) => {
width="50%" width="50%"
label={"کد پستی"} label={"کد پستی"}
regex={(val) => onInput.numberOnly(val)} regex={(val) => onInput.numberOnly(val)}
name="tellphone" name="postalCode"
onChange={(name, value) => onChange(name, value)} onChange={(name, value) => onChange(name, value)}
value={addressFields?.lastName} value={addressFields?.postalCode}
align="text-right" align="text-right"
direction="ltr" direction="ltr"
maxLength={10}
/> />
<div className="flex w-full items-end mt-8 flex-col">
<div className="flex flex-col items-cente w-2/5">
<Button
loading={loading}
title="ثبت آدرس"
backgroundColor="bg-blueC0"
border={{ color: "#196EC055", width: "1px" }}
width="100%"
color="text-white"
onClick={() => submit()}
/>
<button
className="bg-transparent mt-4 text-tiny text-blueC0 py-3 flex flex-row justify-center items-center"
onClick={() => setPhase("deliveryForm")}
>
بازگشت
<img
src={dropdownIcon}
alt=""
className="w-4 transform rotate-90"
/>
</button>
</div>
</div>
</div> </div>
{dialog.open && <Dialog dialog={dialog} />}
</div> </div>
); );
}; };
@ -130,11 +333,15 @@ export const Address = ({ city, province, getCity, getProvince }) => {
const mapStateToProps = (state) => ({ const mapStateToProps = (state) => ({
province: state.publicApi.province, province: state.publicApi.province,
city: state.publicApi.city, city: state.publicApi.city,
provinces: state.publicApi.provinces,
cities: state.publicApi.cities,
loading: state.userAddress.loading,
}); });
const mapDispatchToProps = { const mapDispatchToProps = {
getProvince: publicApi.getProvince, getProvince: publicApi.getProvince,
getCity: publicApi.getCity, getCity: publicApi.getCity,
createAddress: userAddress.add,
}; };
export default connect(mapStateToProps, mapDispatchToProps)(Address); export default connect(mapStateToProps, mapDispatchToProps)(Address);

@ -1,11 +1,19 @@
import React, { useState } from "react"; import React, { useState, useEffect } from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import Button from "../../../components/Button"; import Button from "../../../components/Button";
import Radio from "../../../components/Radio"; import Radio from "../../../components/Radio";
function Address({ addresses, isDark, isMobile, setPhase }) { function Address({ addresses, isDark, isMobile, setPhase }) {
const [activeAddress, setActiveAddress] = useState(addresses[0]); const [activeAddress, setActiveAddress] = useState([]);
const Location = ({ address }) => {
useEffect(() => {
if(addresses?.length > 0){
setActiveAddress(addresses[0]);
}
},[addresses]);
const Location = ({ address, active }) => {
return isMobile ? ( return isMobile ? (
<button <button
className={`flex flex-row border-2 border-solid items-center w-full px-3 py-4 rounded-md mt-4 transition-all duration-500 dark:bg-opacity-5 ${ className={`flex flex-row border-2 border-solid items-center w-full px-3 py-4 rounded-md mt-4 transition-all duration-500 dark:bg-opacity-5 ${
@ -37,9 +45,10 @@ function Address({ addresses, isDark, isMobile, setPhase }) {
? "bg-blueFD border-blueC0" ? "bg-blueFD border-blueC0"
: "bg-grayF9 border-gray45" : "bg-grayF9 border-gray45"
}`} }`}
onClick={() => setActiveAddress(address)}
> >
<div className="flex flex-row gap-3 w-2/3"> <div className="flex flex-row gap-3 flex-1">
<Radio /> <Radio active={active} />
<div className="flex flex-1 flex-col"> <div className="flex flex-1 flex-col">
<li className="text-right text-sm text-blue52 dark:text-white leading-4"> <li className="text-right text-sm text-blue52 dark:text-white leading-4">
آدرس:&nbsp;{address.address} آدرس:&nbsp;{address.address}
@ -50,9 +59,9 @@ function Address({ addresses, isDark, isMobile, setPhase }) {
</li> </li>
</div> </div>
</div> </div>
<div className="flex flex-col w-1/3"> <div className="flex flex-col">
<li className="text-right text-sm text-blue52 dark:text-white"> <li className="text-right text-sm text-blue52 dark:text-white">
نام تحویل گیرنده:&nbsp;{address.receiver} نام تحویل گیرنده:&nbsp;{address.firstName + ' ' + address.lastName}
</li> </li>
<li className="text-right text-sm text-blue52 dark:text-white mt-3"> <li className="text-right text-sm text-blue52 dark:text-white mt-3">
شماره تماس:&nbsp;{address.cellphone} شماره تماس:&nbsp;{address.cellphone}
@ -63,7 +72,9 @@ function Address({ addresses, isDark, isMobile, setPhase }) {
}; };
return isMobile ? ( return isMobile ? (
<section className="mt-2"> <section className="mt-2">
<strong className="font-medium dark:text-white lg:text-tiny">انتخاب آدرس</strong> <strong className="font-medium dark:text-white lg:text-tiny">
انتخاب آدرس
</strong>
<ul className="list-none p-0 m-0 flex flex-col"> <ul className="list-none p-0 m-0 flex flex-col">
{addresses.map((address, index) => ( {addresses.map((address, index) => (
<Location address={address} key={index} /> <Location address={address} key={index} />
@ -86,7 +97,11 @@ function Address({ addresses, isDark, isMobile, setPhase }) {
<strong className="font-medium dark:text-white">انتخاب آدرس</strong> <strong className="font-medium dark:text-white">انتخاب آدرس</strong>
<ul className="list-none p-0 m-0 flex flex-col"> <ul className="list-none p-0 m-0 flex flex-col">
{addresses.map((address, index) => ( {addresses.map((address, index) => (
<Location address={address} key={index} /> <Location
address={address}
key={index}
active={activeAddress === address}
/>
))} ))}
</ul> </ul>
<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">

@ -41,7 +41,7 @@ function TransportMethod({ transportMethods, isDark, isMobile }) {
</button> </button>
) : ( ) : (
<button <button
className={`flex flex-col border border-solid items-center px-2 py-3 rounded-md mt-4 relative transition-all duration-500 w-1/3 ${ className={`flex flex-col border border-solid items-center px-2 py-3 rounded-md mt-4 relative transition-all duration-500 w-2/5 ${
activeMethod === method activeMethod === method
? "bg-blueFD border-blueC0 dark:bg-opacity-5" ? "bg-blueFD border-blueC0 dark:bg-opacity-5"
: "bg-grayF9 border-grayDB dark:bg-opacity-5" : "bg-grayF9 border-grayDB dark:bg-opacity-5"
@ -49,7 +49,7 @@ function TransportMethod({ transportMethods, isDark, isMobile }) {
onClick={() => setActiveMethod(method)} onClick={() => setActiveMethod(method)}
> >
<strong <strong
className={`text-sm transition-all duration-500 ${ className={`text-tiny transition-all duration-500 ${
activeMethod === method activeMethod === method
? "text-blueC0 dark:text-white" ? "text-blueC0 dark:text-white"
: "text-blue52 dark:text-white" : "text-blue52 dark:text-white"
@ -58,7 +58,7 @@ function TransportMethod({ transportMethods, isDark, isMobile }) {
{method.name} {method.name}
</strong> </strong>
<span <span
className={`text-xs transition-all duration-500 mt-2 ${ className={`text-sm transition-all duration-500 mt-2 ${
activeMethod === method activeMethod === method
? "text-blueC0 dark:text-white" ? "text-blueC0 dark:text-white"
: "text-blue52 dark:text-white" : "text-blue52 dark:text-white"

@ -52,7 +52,7 @@ const DesktopHome = ({ grades, getBooks, books }) => {
]; ];
useEffect(() => { useEffect(() => {
getBooks(); getBooks({vod : true});
},[]); },[]);
return ( return (

@ -27,6 +27,7 @@ export const ProductOverAll = ({
pushToCart, pushToCart,
headerOptions, headerOptions,
setHeaderOptions, setHeaderOptions,
loading,
}) => { }) => {
const [type, setType] = useState("1"); const [type, setType] = useState("1");
return ( return (
@ -116,7 +117,7 @@ export const ProductOverAll = ({
</div> </div>
</div> </div>
<div className="flex flex-col w-1/3"> <div className="flex flex-col w-1/3">
<ProductStatusCard price={book?.product[type]?.price} id={book?.product[type]?.id} /> <ProductStatusCard price={book?.product[type]?.price} id={book?.product[type]?.id} loading={loading} />
</div> </div>
</header> </header>
</div> </div>
@ -128,6 +129,7 @@ const mapStateToProps = (state) => ({
grades: state.publicApi.grades, grades: state.publicApi.grades,
isDark: state.publicApi.isDark, isDark: state.publicApi.isDark,
userId: state.user.status.id, userId: state.user.status.id,
loading : state.userProduct.loading
}); });
const mapDispatchToProps = { const mapDispatchToProps = {

@ -34,7 +34,7 @@ export const Products = ({
}; };
useEffect(() => { useEffect(() => {
getList(); getList({vod : true});
setHeaderOptions(headerOptions); setHeaderOptions(headerOptions);
}, []); }, []);

@ -1,13 +1,15 @@
import React from "react"; import React from "react";
import { connect } from "react-redux"; import { connect } from "react-redux";
import { userProduct } from "../../../redux/actions"; import { userProduct } from "../../../redux/actions";
import separate from "../../../utils/separate";
import Loading from "../../../components/Loading";
// 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";
import deleteIcon from "../../../assets/icons/delete.svg"; import deleteIcon from "../../../assets/icons/delete.svg";
function Product({ product, grades, pushToCart, isMobile }) { function Product({ product, grades, pushToCart, isMobile, loading }) {
return isMobile ? ( return isMobile ? (
<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">
@ -60,7 +62,11 @@ function Product({ product, grades, pushToCart, isMobile }) {
+ +
</button> </button>
<span className="mx-2.5 font-semibold text-md text-blueC0"> <span className="mx-2.5 font-semibold text-md text-blueC0">
{product.count} {loading ? (
<Loading size={2} color="bg-green-500" />
) : (
product.count
)}
</span> </span>
<button <button
className="rounded-md pt-2 pb-1.5 px-3 border border-solid border-grayDB text-grayDB dark:border-blueC0 dark:text-blueC0" className="rounded-md pt-2 pb-1.5 px-3 border border-solid border-grayDB text-grayDB dark:border-blueC0 dark:text-blueC0"
@ -84,7 +90,7 @@ function Product({ product, grades, pushToCart, isMobile }) {
</div> </div>
) : ( ) : (
<div className="flex flex-row w-full gap-10 py-4 border-b border-solid border-gray-200"> <div className="flex flex-row w-full gap-10 py-4 border-b border-solid border-gray-200">
<div className="w-1/2 flex flex-row"> <div className="w-3/4 flex flex-row">
<img <img
src={"https://dnvn.ir/api/v1/file/" + product?.product?.book?.fileIds} src={"https://dnvn.ir/api/v1/file/" + product?.product?.book?.fileIds}
className="rounded-md w-20" className="rounded-md w-20"
@ -103,6 +109,12 @@ function Product({ product, grades, pushToCart, isMobile }) {
{product?.productType === 2 ? "نسخه فیزیکی" : "نسخه دیجیتال"} {product?.productType === 2 ? "نسخه فیزیکی" : "نسخه دیجیتال"}
</div> </div>
</div> </div>
<div className="flex flex-row items-center pl-4 mt-6">
<strong className="font-semibold text-xl text-blue5F dark:text-white">
&nbsp;{separate(product?.product?.price * product.count)}
&nbsp;&nbsp;تومان
</strong>
</div>
</div> </div>
</div> </div>
<div className="w-1/4 flex flex-row items-center"> <div className="w-1/4 flex flex-row items-center">
@ -118,26 +130,12 @@ function Product({ product, grades, pushToCart, isMobile }) {
> >
+ +
</button> </button>
<span className="mx-2.5 font-semibold text-lg text-blueC0"> <span className="mx-2.5 font-semibold text-lg text-blueC0 w-8 flex justify-center">
{product.count} {loading ? <Loading size={2} color="bg-green-500" /> : product.count}
</span> </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 text-lg" <>
onClick={() => {product.count === 1 ? (
pushToCart({
productId: product?.productId,
userId: product?.userId,
count: -1,
})
}
>
-
</button>
</div>
<div className="w-1/4 flex flex-row items-center justify-end pl-4">
<strong className="font-semibold text-md text-blue5F dark:text-white">
&nbsp;{product?.product?.price}&nbsp;&nbsp;تومان
</strong>
<img <img
onClick={() => onClick={() =>
pushToCart( pushToCart(
@ -152,8 +150,24 @@ function Product({ product, grades, pushToCart, isMobile }) {
} }
src={deleteIcon} src={deleteIcon}
alt="delete" alt="delete"
className="border border-red-800 border-solid p-1 w-8 bg-red-100 rounded-md mr-4 cursor-pointer" className="border border-red-800 border-solid p-1 w-8 bg-red-100 rounded-md cursor-pointer"
/> />
) : (
<button
className="rounded-md pt-2 pb-1.5 px-3 border border-solid border-blueC0 text-blueC0 dark:border-blueC0 dark:text-blueC0 text-lg"
onClick={() =>
pushToCart({
productId: product?.productId,
userId: product?.userId,
count: -1,
})
}
>
-
</button>
)}
</>
}
</div> </div>
</div> </div>
); );

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { userProduct, userFactor, publicApi } from "../../redux/actions"; import { userProduct, userFactor, publicApi } from "../../redux/actions";
import separate from '../../utils/separate';
import { toast } from "react-toastify";
import DeliveryForm from "../DeliveryForm"; import DeliveryForm from "../DeliveryForm";
import Product from "./Product"; import Product from "./Product";
import Address from "../Address"; import Address from "../Address";
@ -28,6 +29,8 @@ function ShoppingCart({
headerOptions, headerOptions,
setHeaderOptions, setHeaderOptions,
desktopContentTransform, desktopContentTransform,
userProducts,
loading,
}) { }) {
const [phase, setPhase] = useState("products"); const [phase, setPhase] = useState("products");
@ -37,8 +40,15 @@ function ShoppingCart({
setHeaderOptions(headerOptions); setHeaderOptions(headerOptions);
}, []); }, []);
const productsLength = userProducts?.filter(
(product) => product.condition < 2
)?.length;
const next = { const next = {
products: () => setPhase("deliveryForm"), products: () =>
productsLength
? setPhase("deliveryForm")
: toast.info("ابتدا یک محصول را به سبد خرید خود اضافه کنید."),
deliveryForm: () => console.log("Go to shaparak"), deliveryForm: () => console.log("Go to shaparak"),
profile: () => { profile: () => {
console.log("Set profile"); console.log("Set profile");
@ -50,20 +60,19 @@ function ShoppingCart({
return ( return (
<div className="w-full flex flex-col"> <div className="w-full flex flex-col">
<header className="w-full flex flex-row gap-10"> <header className="w-full flex flex-row gap-10">
<strong className="w-1/2 pb-2 border-b border-solid border-gray-200 text-gray70 text-sm font-normal"> <strong className="w-3/4 pb-2 border-b border-solid border-gray-200 text-gray70 text-sm font-normal">
کالاهای انتخابی{" "} کالاهای انتخابی{" "}
</strong> </strong>
<strong className="w-1/4 pb-2 border-b border-solid border-gray-200 text-gray70 text-sm font-normal"> <strong className="w-1/4 pb-2 border-b border-solid border-gray-200 text-gray70 text-sm font-normal">
تعداد سفارش تعداد سفارش
</strong> </strong>
<strong className="w-1/4 pb-2 border-b border-solid border-gray-200 text-gray70 text-sm font-normal">
مبلغ
</strong>
</header> </header>
<ul className="w-full mt-2 list-none p-0"> <ul className="w-full mt-2 list-none p-0">
{list.map( {list.map(
(product, index) => (product, index) =>
product.condition < 2 && <Product key={index} product={product} /> product.condition < 2 && (
<Product loading={loading} key={index} product={product} />
)
)} )}
</ul> </ul>
</div> </div>
@ -94,7 +103,7 @@ function ShoppingCart({
color: !isDark ? (type === "error" ? "#F1420D" : "#246E8A") : "", color: !isDark ? (type === "error" ? "#F1420D" : "#246E8A") : "",
}} }}
> >
{value} {separate(value)}
<span <span
style={{ style={{
color: !isDark ? (type === "error" ? "#F1420D" : "#246E8A") : "", color: !isDark ? (type === "error" ? "#F1420D" : "#246E8A") : "",
@ -109,7 +118,11 @@ function ShoppingCart({
}; };
const layouts = { const layouts = {
products: <Table />, products: productsLength ? (
<Table />
) : (
<strong className="text-blueC0">سبد خرید شما خالی است.</strong>
),
deliveryForm: <DeliveryForm setPhase={setPhase} />, deliveryForm: <DeliveryForm setPhase={setPhase} />,
address: <Address setPhase={setPhase} />, address: <Address setPhase={setPhase} />,
}; };
@ -173,22 +186,24 @@ function ShoppingCart({
{phase !== "address" && ( {phase !== "address" && (
<div <div
className="flex flex-col w-1/4 p-10 bg-grayF9 border border-solid border-grayDB rounded-md px-4 py-4" className="flex flex-col w-1/4 p-10 bg-grayF9 border border-solid border-grayDB rounded-md px-4 py-4"
style={{ height: "fit-content" }} style={{ height: "fit-content", minWidth: 350}}
> >
<div className="flex flex-col w-full border-t py-2 border-gray-300 border-b"> <div className="flex flex-col w-full border-t py-2 border-gray-300 border-b">
<PriceStatus <PriceStatus
name="مبلغ سبد خرید" name="مبلغ سبد خرید"
value={factorInfo?.sumPrice} value={productsLength ? factorInfo?.sumPrice : 0}
type="normal" type="normal"
/> />
<PriceStatus <PriceStatus
name="میزان تخفیف" name="میزان تخفیف"
value={factorInfo?.offPrice} value={productsLength ? factorInfo?.offPrice : 0}
type="error" type="error"
/> />
<PriceStatus <PriceStatus
name="مالیات بر ارزش افزوده" name="مالیات بر ارزش افزوده"
value={Math.floor(factorInfo?.sumPrice * 0.009)} value={
productsLength ? Math.floor(factorInfo?.sumPrice * 0.009) : 0
}
type="error" type="error"
/> />
</div> </div>
@ -197,7 +212,7 @@ function ShoppingCart({
جمع کل سبد خرید جمع کل سبد خرید
</strong> </strong>
<p className="font-medium text-greenBA text-xl dark:text-white"> <p className="font-medium text-greenBA text-xl dark:text-white">
{factorInfo?.payPrice} {separate(productsLength ? factorInfo?.payPrice : 0)}
<span className="font-medium text-greenBA text-sm mr-2"> <span className="font-medium text-greenBA text-sm mr-2">
تومان تومان
</span> </span>
@ -247,6 +262,7 @@ function ShoppingCart({
width="100%" width="100%"
color="text-white" color="text-white"
onClick={next[phase]} onClick={next[phase]}
// loading={loading}
/> />
{phase === "deliveryForm" && ( {phase === "deliveryForm" && (
<button <button
@ -276,6 +292,8 @@ const mapStateToProps = (state) => ({
user: state.user.status, user: state.user.status,
hasPhysical: state.userProduct.hasPhysical, hasPhysical: state.userProduct.hasPhysical,
desktopContentTransform: state.publicApi.desktopContentTransform, desktopContentTransform: state.publicApi.desktopContentTransform,
userProducts: state.userProduct.list,
loading: state.userProduct.loading
}); });
const mapDispatchToProps = { const mapDispatchToProps = {

@ -143,7 +143,7 @@ function Video({
<div className="w-3/12 flex flex-col"> <div className="w-3/12 flex flex-col">
<div className="w-full flex flex-col pr-2 py-2 border-0"> <div className="w-full flex flex-col pr-2 py-2 border-0">
<h1 className="text-blue5F mb-3 font-semibold text-xl dark:text-white"> <h1 className="text-blue5F mb-3 font-semibold text-xl dark:text-white">
{book?.name} {'دوره ویدئویی' + ' ' + book?.name}
</h1> </h1>
<div className="text-blueC0 text-sm dark:text-greenBA"> <div className="text-blueC0 text-sm dark:text-greenBA">
{"پایه" + " " + grades[book?.gradeId]} {"پایه" + " " + grades[book?.gradeId]}

@ -4,54 +4,52 @@ module.exports = {
theme: { theme: {
backgroundColor: (theme) => ({ backgroundColor: (theme) => ({
...theme("colors"), ...theme("colors"),
blueC0: "#196EC0", blueC0: "#196EC0", //در سبد خرید - دکمه ادامه فرآیند
blueB0: "#517AB0", blueB0: "#517AB0", // صفحه هوم دسکتاپ - زیر اسلایدر متن لیست محصولات
blue90: "#235A90", blue90: "#235A90", // نقطه های مشخص کننده صفحه در دسکتاپ انیمیشن حالت خاموش
blueCB: "#98B2CB", blueCB: "#98B2CB", // صفحه فعال سازی کتاب دیجیتال دکمه فعال سازی با کد QR
blueEB: "#2B8DEB", blueEB: "#2B8DEB", // نوار پروگرس مقدار داخلی که داره پر میشه
blueFF: "#E8FCFF", blueFF: "#E8FCFF", // در موبایل صفحه ویديو رنگ پس زمینه لیست فصل ها
blueFF1: "#E9F0FF", blueFF1: "#E9F0FF", // در صفحه نمایش ویديو پس زمینه پلی لیست
blueFD: "#F6F9FD", blueFD: "#F6F9FD", // در صفحه فعال سازی کتاب پس زمینه هشدار
blueFD1 : "#F4F8FD", blueFD1 : "#F4F8FD", // در صفحه فعال سازی هدر بالای سایت
blueFFOP : "rgba(6, 146, 255, 0.36)", blueFFOP : "rgba(6, 146, 255, 0.36)", // در صفحه محصولات دکمه پاک کردن فیلترها در سایدبار
redFF: "#FFE6E6", redFF: "#FFE6E6", // در انیمیشن موبایل که میاد کد رو اسکن کنه نتیجه اسکن پس زمینه پی دی اف
redFF1: "#FF2020", redFF1: "#FF2020", // در آیکن همبرگری نقطه نوتیف
red19: "#C01919", red19: "#C01919", // در صفحه محصول رنگ متن مشاهده نمونه
yellow2: "#FDFFDA", yellow2: "#FDFFDA", // در انیمیشن موبایل که برای اسکن کد میاد نتیجه اسکن پس زمینه لینک ها
yellow3: "#EFFF00", yellow3: "#EFFF00", // خط زیر متن یادگیری در دیزاین موبایل پابلیک صفحه هوم
gray36: "#363636", gray36: "#363636", // در حالت دارک صفحه موبایل پس زمینه دکمه های کیو آر و ای آز در هدر
orange1: "#FF6600", orange1: "#FF6600", // خط زیر متن کنارتونیم در دیزاین موبایل پابلیک صفحه هوم
greenBA: "#06BACE", greenBA: "#06BACE", // در سرچ اصلی سایت رنگ تکست تایتل محصولات و صفحات
green1: "#00FF80", green1: "#00FF80", // خط زیر متن یادگیری جذاب در دیزاین موبایل پابلیک صفحه هوم
gray20: "#202020", gray20: "#202020", // هیچ جا
gray2077: "#20202077", gray2077: "#20202077", // در صفحه محصولات حالت دارک صفحه محصولات پس زمینه سرچ
gray2C: "#2C2C2C", gray2C: "#2C2C2C", // هیچ جا
gray36: "#363636", gray50: "#505050", // در صفحه اسکن کیو آر رنگ پس زمینه ی روی عکس
gray50: "#505050", grayE3: "#E3E3E3", // رنگ پس زمینه لپ تاپ در صفحه هوم موبایل که نشون دهنده ریسپانسیو بودن سایت ماست
grayDB: "#DBDBDB", grayEE: "#EEEEEE", // رنگ پس زمینه موبایل در صفحه هوم موبایل که نشون دهنده ریسپانسیو بودن سایت ماس
grayE3: "#E3E3E3", grayF9: "#F9F9F9", // رنگ پس زمینه قیمت محصول و گارنتی و فلان در سبد خرید
grayEE: "#EEEEEE", grayF4: "#EBF0F4", // در صفحه محصولات رنگ پس زمینه عکس کتاب
grayF9: "#F9F9F9", grayF7: "#F7F7F7" // رنگ پس زمینه باکس اضافه کردن به سبد خرید در صفحه محصول
grayF4: "#EBF0F4",
grayF7: "#F7F7F7"
}), }),
textColor: (theme) => ({ textColor: (theme) => ({
...theme("colors"), ...theme("colors"),
primary: "#235A90", primary: "#235A90",
blueC0: "#196EC0", blueC0: "#196EC0", //در سبد خرید - دکمه ادامه فرآیند
blueC01: "#1999C0", blueC01: "#1999C0", // هیچ جا
blueE8: "#2E81E8", blueE8: "#2E81E8", // در صفحه محصول متن به من کمک کن انتخاب کنم
blue52: "#132F52", blue52: "#132F52", // در باکس اضافه کردن به سبد خرید رنگ متن با خرید این محصول فلان تومن سود کن
blue5F: "#05335F", blue5F: "#05335F", // رنگ لینکها در هدر بالای سایت
blueB9: "#86A0B9", blueB9: "#86A0B9", // در موبایل تب پایین صفحه رنگ متن صفحات
green71: "#067182", green71: "#067182", // در دسکتاپ انیمیشن قسمت برگزاری آزمون تکست زمان بافیمانده
greenBA: "#06BACE", greenBA: "#06BACE", // در سرچ اصلی سایت رنگ تکست تایتل محصولات و صفحات
red5F: "#5F1A05", red5F: "#5F1A05", // در انیمیشن موبایل که میاد کد رو اسکن کنه نتیجه اسکن متن پی دی اف
red19: "#C01919", red19: "#C01919", // در صفحه محصول رنگ متن مشاهده نمونه
yellow1: "#5F5F05", yellow1: "#5F5F05", // در انیمیشن موبایل که برای اسکن کد میاد نتیجه اسکن رنگ متن لینک ه
gray1: "#333333", gray1: "#333333", // رنگ متن سرچ در صفحه محصولات موبایل
gray70: "#707070", gray70: "#707070", // رنگ متن تایتل لیست لینک ها در فوتر سایت
grayDB: "#DBDBDB", grayDB: "#DBDBDB", // در دراور موبایل رنگ متن شماره موبایل کاربر در حالت دارک
}), }),
// height: (theme) =>( { // height: (theme) =>( {
// sm: '8px', // sm: '8px',

Loading…
Cancel
Save