| @@ -52,7 +52,7 @@ | |||
| }, | |||
| "scripts": { | |||
| "start": "react-scripts start", | |||
| "build": "react-scripts build", | |||
| "build": "set \"GENERATE_SOURCEMAP=false\" && react-scripts build", | |||
| "test": "react-scripts test", | |||
| "eject": "react-scripts eject", | |||
| "json-serve": "json-server ./db/db.js --port=4000" | |||
| @@ -44,7 +44,7 @@ | |||
| work correctly both with client-side routing and a non-root public URL. | |||
| Learn how to configure a non-root public URL by running `npm run build`. | |||
| --> | |||
| <title>React App</title> | |||
| <title>Trampa</title> | |||
| </head> | |||
| <body> | |||
| <noscript>You need to enable JavaScript to run this app.</noscript> | |||
| @@ -12,7 +12,8 @@ const FilterFooter = (props) => { | |||
| const filters = props.filters; | |||
| const handleFilters = () => { | |||
| filters.paging.changePage(1); | |||
| filters.apply(); | |||
| filters.search.clear(); | |||
| filters.applyFilters(); | |||
| props.toggleFilters(); | |||
| }; | |||
| const clearFilters = () => { | |||
| @@ -36,7 +36,7 @@ const ImagesCarousel = (props) => { | |||
| <CloseButton onClick={closeCreateOfferModal}> | |||
| <CloseButtonIcon /> | |||
| </CloseButton> | |||
| <Scroller> | |||
| <Scroller isCarousel> | |||
| {props?.offer?.offer?.images.map((image) => { | |||
| if (!image) return; | |||
| return ( | |||
| @@ -79,7 +79,7 @@ const Header = () => { | |||
| search: newQueryString.toString(), | |||
| }); | |||
| } else { | |||
| search.searchOffers(value); | |||
| search.searchOffersImmediately(value); | |||
| } | |||
| }; | |||
| @@ -1,4 +1,4 @@ | |||
| import React from "react"; | |||
| import React, { useCallback } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| EndIcon, | |||
| @@ -6,18 +6,28 @@ import { | |||
| SearchInputContainer, | |||
| } from "./SearchInput.styled"; | |||
| import { forwardRef } from "react"; | |||
| import { useCallback } from "react"; | |||
| // import { useCallback } from "react"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import { routeMatches } from "../../../util/helpers/routeHelpers"; | |||
| import { ABOUT_PAGE } from "../../../constants/pages"; | |||
| import { ABOUT_PAGE, BASE_PAGE, HOME_PAGE } from "../../../constants/pages"; | |||
| import { debounceHelper } from "../../../util/helpers/debounceHelper"; | |||
| const SearchInput = forwardRef((props, ref) => { | |||
| const { t } = useTranslation(); | |||
| const handleSearch = () => { | |||
| if (routeMatches(HOME_PAGE) || routeMatches(BASE_PAGE)) { | |||
| debounceHelper(() => props.handleSearch(ref.current.value), 500); | |||
| } | |||
| }; | |||
| const handleManualSearch = () => { | |||
| debounceHelper(() => {}, 500); | |||
| props.handleSearch(ref.current.value); | |||
| }; | |||
| const listener = useCallback( | |||
| (event) => { | |||
| if (event.keyCode === 13) { | |||
| event.preventDefault(); | |||
| props.handleSearch(ref.current.value); | |||
| handleManualSearch(); | |||
| } | |||
| }, | |||
| [ref.current] | |||
| @@ -37,10 +47,11 @@ const SearchInput = forwardRef((props, ref) => { | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <EndIcon size="36px"> | |||
| <SearchIcon onClick={() => props.handleSearch(ref.current.value)} /> | |||
| <SearchIcon onClick={handleManualSearch} /> | |||
| </EndIcon> | |||
| ), | |||
| }} | |||
| onChange={handleSearch} | |||
| placeholder={t("header.searchOffers")} | |||
| onFocus={handleFocusSearch} | |||
| onBlur={handleBlurSearch} | |||
| @@ -5,9 +5,11 @@ import { | |||
| ListContainer, | |||
| } from "./HorizontalScroller.styled"; | |||
| import { ArrowButton } from "../Buttons/ArrowButton/ArrowButton"; | |||
| import useIsMobile from "../../hooks/useIsMobile"; | |||
| const HorizontalScroller = (props) => { | |||
| const scrollRef = useRef(null); | |||
| const { isMobile } = useIsMobile(); | |||
| const [isDisabledLeftButton, setIsDisabledLeftButton] = useState(true); | |||
| const [isDisabledRightButton, setIsDisabledRightButton] = useState(false); | |||
| @@ -39,7 +41,15 @@ const HorizontalScroller = (props) => { | |||
| ) { | |||
| if (isDisabledRightButton !== true) setIsDisabledRightButton(true); | |||
| } | |||
| scrollRef.current.scrollBy({ left: 50, behaviour: "smooth" }); | |||
| if (props.isCarousel) { | |||
| if (isMobile) { | |||
| scrollRef.current.scrollBy({ left: 239, behaviour: "smooth" }); | |||
| } else { | |||
| scrollRef.current.scrollBy({ left: 599, behaviour: "smooth" }); | |||
| } | |||
| } else { | |||
| scrollRef.current.scrollBy({ left: 50, behaviour: "smooth" }); | |||
| } | |||
| }; | |||
| const handleLeft = () => { | |||
| if (scrollRef.current.scrollLeft - 50 <= 0) { | |||
| @@ -51,7 +61,15 @@ const HorizontalScroller = (props) => { | |||
| ) { | |||
| if (isDisabledRightButton !== false) setIsDisabledRightButton(false); | |||
| } | |||
| scrollRef.current.scrollBy({ left: -50, behaviour: "smooth" }); | |||
| if (props.isCarousel) { | |||
| if (isMobile) { | |||
| scrollRef.current.scrollBy({ left: -239, behaviour: "smooth" }); | |||
| } else { | |||
| scrollRef.current.scrollBy({ left: -599, behaviour: "smooth" }); | |||
| } | |||
| } else { | |||
| scrollRef.current.scrollBy({ left: -50, behaviour: "smooth" }); | |||
| } | |||
| }; | |||
| return ( | |||
| <HorizontalScrollerContainer | |||
| @@ -89,6 +107,7 @@ HorizontalScroller.propTypes = { | |||
| containerStyle: PropTypes.any, | |||
| listStyle: PropTypes.any, | |||
| hideArrows: PropTypes.bool, | |||
| isCarousel: PropTypes.bool, | |||
| }; | |||
| export default HorizontalScroller; | |||
| @@ -140,7 +140,7 @@ const useOffers = () => { | |||
| // Those hooks are below becouse function apply cannot be put on props before initialization | |||
| const sorting = useSorting(apply); | |||
| const paging = usePaging(apply); | |||
| const search = useSearch(apply); | |||
| const search = useSearch(applyFilters); | |||
| // On every change of search string, offers should be immediately searched | |||
| useEffect(() => { | |||
| @@ -167,6 +167,7 @@ const useOffers = () => { | |||
| filters, | |||
| sorting, | |||
| paging, | |||
| search, | |||
| queryStringHook, | |||
| allOffersToShow, | |||
| totalOffers, | |||
| @@ -52,6 +52,11 @@ const useSearch = (applyAllFilters) => { | |||
| setSearchStringLocally(searchValue); | |||
| }; | |||
| const searchOffersImmediately = (searchValue) => { | |||
| searchOffers(searchValue); | |||
| applyAllFilters(); | |||
| }; | |||
| const clear = () => { | |||
| setSearchStringLocally(""); | |||
| }; | |||
| @@ -59,6 +64,7 @@ const useSearch = (applyAllFilters) => { | |||
| return { | |||
| searchOffers, | |||
| setSearchStringLocally, | |||
| searchOffersImmediately, | |||
| searchStringLocally, | |||
| searchString, | |||
| clear, | |||
| @@ -6,13 +6,13 @@ const ErrorMessage = (props) => { | |||
| const formik = props.formik; | |||
| return ( | |||
| <> | |||
| {formik.errors?.location ? ( | |||
| {formik.errors?.location && formik.touched?.location ? ( | |||
| <ErrorMessageContainer>{formik.errors.location}</ErrorMessageContainer> | |||
| ) : formik.errors?.phoneNumber ? ( | |||
| ) : formik.errors?.phoneNumber && formik.touched?.phoneNumber ? ( | |||
| <ErrorMessageContainer> | |||
| {formik.errors.phoneNumber} | |||
| </ErrorMessageContainer> | |||
| ) : formik.errors?.website ? ( | |||
| ) : formik.errors?.website && formik.touched?.website ? ( | |||
| <ErrorMessageContainer>{formik.errors.website}</ErrorMessageContainer> | |||
| ) : ( | |||
| <></> | |||
| @@ -43,7 +43,6 @@ const ThirdPartOfRegistration = (props) => { | |||
| initialValues: thirdPartInitialValues(props.informations), | |||
| validationSchema: thirdPartValidation(locations), | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| }); | |||
| @@ -0,0 +1,7 @@ | |||
| let debounceFn; | |||
| export const debounceHelper = (fn, delay) => { | |||
| if (debounceFn) clearTimeout(debounceFn); | |||
| debounceFn = setTimeout(() => { | |||
| fn(); | |||
| }, delay); | |||
| }; | |||
| @@ -20,7 +20,6 @@ export const routeMatches = (route) => { | |||
| export const replaceInRoute = (route, pathVariables = {}) => { | |||
| const keys = Object.keys(pathVariables); | |||
| return keys.reduce( | |||
| (acc, key) => acc.replace(`:${key}`, pathVariables[`${key}`]), | |||
| route | |||
| @@ -12,6 +12,7 @@ export default (locations) => | |||
| i18next.t("register.labelLocationValid") | |||
| ), | |||
| website: Yup.string().matches( | |||
| /^((ftp|http|https):\/\/)?(www.)?(?!.*(ftp|http|https|www.))[a-zA-Z0-9_-]+(\.[a-zA-Z]+)+((\/)[\w#]+)*(\/\w+\?[a-zA-Z0-9_]+=\w+(&[a-zA-Z0-9_]+=\w+)*)?$/gm | |||
| /^((ftp|http|https):\/\/)?(www.)?(?!.*(ftp|http|https|www.))[a-zA-Z0-9_-]+(\.[a-zA-Z]+)+((\/)[\w#]+)*(\/\w+\?[a-zA-Z0-9_]+=\w+(&[a-zA-Z0-9_]+=\w+)*)?$/gm, | |||
| i18n.t("register.websiteError") | |||
| ), | |||
| }); | |||