diff --git a/src/components/Button/index.js b/src/components/Button/index.js index f205980..bab8788 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -1,4 +1,5 @@ import React from "react"; +import Loading from "../Loading"; export default function Button({ title, @@ -11,13 +12,11 @@ export default function Button({ selectable, active, borderType, - twPaddings, - customStyle, - radius + loading, }) { return ( ); } diff --git a/src/components/Dialog/index.js b/src/components/Dialog/index.js new file mode 100644 index 0000000..c5b5ce3 --- /dev/null +++ b/src/components/Dialog/index.js @@ -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 ( +
setOpen(false)} + > +
+ {/* {type === "confirm" && ( +
exit
+ )} */} +

{text}

+ {type === "confirm" && ( +
+ + +
+ )} + {type === "alert" && ( +
+ +
+ )} +
+
+ ); +}; + +const mapStateToProps = (state) => ({}); + +export default connect(mapStateToProps)(Dialog); diff --git a/src/components/Input/index.js b/src/components/Input/index.js index 9201d8f..f768b79 100644 --- a/src/components/Input/index.js +++ b/src/components/Input/index.js @@ -19,14 +19,13 @@ const inputStatusHandler = (value, focus, status, primary, success, danger) => { if (status) { return success; } else { - return danger; + return; } } } }; export default function Input(props) { - const [value, setValue] = useState(""); const [focusToggle, setFocusToggle] = useState(false); const { @@ -44,6 +43,7 @@ export default function Input(props) { regex, direction, width, + value, } = props; return ( @@ -93,6 +93,7 @@ export default function Input(props) { ], " " )} + value={value} style={{ direction: direction }} onChange={(e) => onChange(name, (e.target.value = regex(e.target.value))) @@ -101,7 +102,7 @@ export default function Input(props) { onBlur={() => (!value ? setFocusToggle(false) : setFocusToggle(true))} maxLength={maxLength} /> - {!status && value && ( + {/* {!status && value && ( {langDetector(lang, - )} - {status && value && ( + )} */} + {/* {status && value && ( {langDetector(lang, - )} + )} */} {value && !status && ( )} - {value && ( + {/* {value && ( {value.length + "/" + maxLength} - )} + )} */} ); } diff --git a/src/components/Loading/index.js b/src/components/Loading/index.js new file mode 100644 index 0000000..5c26ce7 --- /dev/null +++ b/src/components/Loading/index.js @@ -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 ( +
+ + + +
+ ); +} + +export default Loading; + +Loading.defaultProps = { + size: 2, +}; diff --git a/src/components/Loading/index.scss b/src/components/Loading/index.scss new file mode 100644 index 0000000..f5765e4 --- /dev/null +++ b/src/components/Loading/index.scss @@ -0,0 +1,12 @@ +.loading-animation{ + animation: loading 0.6s infinite; +} + +@keyframes loading{ + 0%{ + opacity: 0.5; + } + 100%{ + opacity: 1; + } +} \ No newline at end of file diff --git a/src/components/ProductStatusCard/index.js b/src/components/ProductStatusCard/index.js index 0a3cdd9..9963b13 100644 --- a/src/components/ProductStatusCard/index.js +++ b/src/components/ProductStatusCard/index.js @@ -22,6 +22,7 @@ const ProductStatusCard = ({ userId, isLogin, pushToCart, + loading, }) => { return (
@@ -69,6 +70,7 @@ const ProductStatusCard = ({ width="100%" color={"text-white"} selectable={true} + loading={loading} onClick={() => isLogin ? pushToCart({ diff --git a/src/components/Radio/index.js b/src/components/Radio/index.js index bc65e2a..fa8b60f 100644 --- a/src/components/Radio/index.js +++ b/src/components/Radio/index.js @@ -4,7 +4,7 @@ import "./index.scss"; export default function Radio(props) { return (
- + diff --git a/src/components/ReverseMap/index.js b/src/components/ReverseMap/index.js new file mode 100644 index 0000000..10ba755 --- /dev/null +++ b/src/components/ReverseMap/index.js @@ -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( + + ); + setMarkerArray(array); + } + return ( + + {markerArray} + + ); +}; +export default ReverseMap; diff --git a/src/components/Select/index.js b/src/components/Select/index.js index a392a68..ca73792 100644 --- a/src/components/Select/index.js +++ b/src/components/Select/index.js @@ -41,4 +41,5 @@ export default Select; Select.defaultProps = { options: ["Really", "Option 2", "Option 3"], + borderColor : 'border-gray-300' }; diff --git a/src/components/VodProduct/index.js b/src/components/VodProduct/index.js index d81bce8..6b6c93d 100644 --- a/src/components/VodProduct/index.js +++ b/src/components/VodProduct/index.js @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; -import {Link} from 'react-router-dom'; -import separate from '../../utils/separate'; +import { Link } from "react-router-dom"; +import separate from "../../utils/separate"; import StarRating from "../StarRating"; import ReactTooltip from "react-tooltip"; @@ -68,7 +68,7 @@ const VodProduct = ({ <> {/* product UI Design */} @@ -146,7 +146,7 @@ const VodProduct = ({ {console.log(video?.gradeId)} - { 'پایه' + ' ' + grades[video?.gradeId]} + {"پایه" + " " + grades[video?.gradeId]}
{productContent && ( @@ -175,9 +175,7 @@ const VodProduct = ({ : "flex" }`} > - - - + {/* {discountPrice && (

@@ -185,12 +183,12 @@ const VodProduct = ({

)} */} - {/*

- {separate(video?.price) + ' '} + {separate(video?.product[2]?.price) + ' '} تومان -

*/} +

{/* add to cart */} {addToCartHolder && ( diff --git a/src/views/404/index.js b/src/views/404/index.js index 28989a5..16e4c6b 100644 --- a/src/views/404/index.js +++ b/src/views/404/index.js @@ -19,9 +19,9 @@ function NotFound({ headerOptions, setHeaderOptions }) { 404

- وای!! + اوه!!
- صفحه ای که بدنبال آن بوده‌اید دیگر وجود ندارد :( + دوست من، اشتباه اومدی

); diff --git a/src/views/Address/index.js b/src/views/Address/index.js index 8d85a89..dae5302 100644 --- a/src/views/Address/index.js +++ b/src/views/Address/index.js @@ -1,12 +1,37 @@ import React, { useState, useEffect } from "react"; import { connect } from "react-redux"; -import { publicApi } from "../../redux/actions"; +import { publicApi, userAddress } from "../../redux/actions"; import onInput from "../../utils/onInput"; import Input from "../../components/Input"; 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 [mapAddress, setMapAddress] = useState(null); + const [dialog, setDialog] = useState({ + text: null, + type: null, + setOpen: null, + accept: null, + color: null, + open: null, + }); useEffect(() => { if (province.length <= 1) { @@ -16,11 +41,144 @@ export const Address = ({ city, province, getCity, getProvince }) => { useEffect(() => { if (addressFields?.provinceId) { - getCity({ provinceId: addressFields?.provinceId }); + getCity({ + provinceId: provinces.filter( + (province) => province.name === addressFields?.provinceId + )[0].id, + }); } }, [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 }) => { return ( @@ -33,9 +191,26 @@ export const Address = ({ city, province, getCity, getProvince }) => { return (
-
+
+
+ setMapAddress(val)} /> +
+ onInput.numberOnly(val)} + name="address" + onChange={(name, value) => setMapAddress(value)} + value={mapAddress} + align="text-right" + direction="rtl" + /> +
{ value={addressFields?.cellphone} align="text-right" direction="ltr" + maxLength={11} /> onInput.numberOnly(val)} - name="tellphone" + name="phone" onChange={(name, value) => onChange(name, value)} - value={addressFields?.lastName} + value={addressFields?.phone} align="text-right" direction="ltr" + maxLength={11} />

@@ -116,13 +293,39 @@ export const Address = ({ city, province, getCity, getProvince }) => { width="50%" label={"کد پستی"} regex={(val) => onInput.numberOnly(val)} - name="tellphone" + name="postalCode" onChange={(name, value) => onChange(name, value)} - value={addressFields?.lastName} + value={addressFields?.postalCode} align="text-right" direction="ltr" + maxLength={10} /> +
+
+ +
+
+ {dialog.open && } ); }; @@ -130,11 +333,15 @@ export const Address = ({ city, province, getCity, getProvince }) => { const mapStateToProps = (state) => ({ province: state.publicApi.province, city: state.publicApi.city, + provinces: state.publicApi.provinces, + cities: state.publicApi.cities, + loading: state.userAddress.loading, }); const mapDispatchToProps = { getProvince: publicApi.getProvince, getCity: publicApi.getCity, + createAddress: userAddress.add, }; export default connect(mapStateToProps, mapDispatchToProps)(Address); diff --git a/src/views/DeliveryForm/Address/index.js b/src/views/DeliveryForm/Address/index.js index 31a125b..2ddb8fb 100644 --- a/src/views/DeliveryForm/Address/index.js +++ b/src/views/DeliveryForm/Address/index.js @@ -1,11 +1,19 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { connect } from "react-redux"; import Button from "../../../components/Button"; import Radio from "../../../components/Radio"; function Address({ addresses, isDark, isMobile, setPhase }) { - const [activeAddress, setActiveAddress] = useState(addresses[0]); - const Location = ({ address }) => { + const [activeAddress, setActiveAddress] = useState([]); + + useEffect(() => { + if(addresses?.length > 0){ + setActiveAddress(addresses[0]); + } + + },[addresses]); + + const Location = ({ address, active }) => { return isMobile ? ( ) : ( - {product.count} + {loading ? ( + + ) : ( + product.count + )} - - {product.count} + + {loading ? : product.count} - - -
- -  {product?.product?.price}  تومان - - - pushToCart( - { - productId: product?.productId, - userId: product?.userId, - count: product?.productType === 1 ? -1 : -product?.count, - }, - {}, - { id: product?.factorId } - ) - } - src={deleteIcon} - alt="delete" - className="border border-red-800 border-solid p-1 w-8 bg-red-100 rounded-md mr-4 cursor-pointer" - /> + { + <> + {product.count === 1 ? ( + + pushToCart( + { + productId: product?.productId, + userId: product?.userId, + count: product?.productType === 1 ? -1 : -product?.count, + }, + {}, + { id: product?.factorId } + ) + } + src={deleteIcon} + alt="delete" + className="border border-red-800 border-solid p-1 w-8 bg-red-100 rounded-md cursor-pointer" + /> + ) : ( + + )} + + }
); diff --git a/src/views/ShoppingCart/index.js b/src/views/ShoppingCart/index.js index bbb3ef1..c579569 100644 --- a/src/views/ShoppingCart/index.js +++ b/src/views/ShoppingCart/index.js @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import { userProduct, userFactor, publicApi } from "../../redux/actions"; - +import separate from '../../utils/separate'; +import { toast } from "react-toastify"; import DeliveryForm from "../DeliveryForm"; import Product from "./Product"; import Address from "../Address"; @@ -28,6 +29,8 @@ function ShoppingCart({ headerOptions, setHeaderOptions, desktopContentTransform, + userProducts, + loading, }) { const [phase, setPhase] = useState("products"); @@ -37,8 +40,15 @@ function ShoppingCart({ setHeaderOptions(headerOptions); }, []); + const productsLength = userProducts?.filter( + (product) => product.condition < 2 + )?.length; + const next = { - products: () => setPhase("deliveryForm"), + products: () => + productsLength + ? setPhase("deliveryForm") + : toast.info("ابتدا یک محصول را به سبد خرید خود اضافه کنید."), deliveryForm: () => console.log("Go to shaparak"), profile: () => { console.log("Set profile"); @@ -50,20 +60,19 @@ function ShoppingCart({ return (
- + کالاهای انتخابی{" "} تعداد سفارش - - مبلغ -
    {list.map( (product, index) => - product.condition < 2 && + product.condition < 2 && ( + + ) )}
@@ -94,7 +103,7 @@ function ShoppingCart({ color: !isDark ? (type === "error" ? "#F1420D" : "#246E8A") : "", }} > - {value} + {separate(value)} , + products: productsLength ? ( + + ) : ( + سبد خرید شما خالی است. + ), deliveryForm: , address:
, }; @@ -173,22 +186,24 @@ function ShoppingCart({ {phase !== "address" && (
@@ -197,7 +212,7 @@ function ShoppingCart({ جمع کل سبد خرید

- {factorInfo?.payPrice} + {separate(productsLength ? factorInfo?.payPrice : 0)} تومان @@ -247,6 +262,7 @@ function ShoppingCart({ width="100%" color="text-white" onClick={next[phase]} + // loading={loading} /> {phase === "deliveryForm" && (