| }, | }, | ||||
| "scripts": { | "scripts": { | ||||
| "start": "react-scripts start", | "start": "react-scripts start", | ||||
| "build": "react-scripts build", | |||||
| "build": "set \"GENERATE_SOURCEMAP=false\" && react-scripts build", | |||||
| "test": "react-scripts test", | "test": "react-scripts test", | ||||
| "eject": "react-scripts eject", | "eject": "react-scripts eject", | ||||
| "json-serve": "json-server ./db/db.js --port=4000" | "json-serve": "json-server ./db/db.js --port=4000" |
| work correctly both with client-side routing and a non-root public URL. | 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`. | Learn how to configure a non-root public URL by running `npm run build`. | ||||
| --> | --> | ||||
| <title>React App</title> | |||||
| <title>Trampa</title> | |||||
| </head> | </head> | ||||
| <body> | <body> | ||||
| <noscript>You need to enable JavaScript to run this app.</noscript> | <noscript>You need to enable JavaScript to run this app.</noscript> |
| const filters = props.filters; | const filters = props.filters; | ||||
| const handleFilters = () => { | const handleFilters = () => { | ||||
| filters.paging.changePage(1); | filters.paging.changePage(1); | ||||
| filters.apply(); | |||||
| filters.search.clear(); | |||||
| filters.applyFilters(); | |||||
| props.toggleFilters(); | props.toggleFilters(); | ||||
| }; | }; | ||||
| const clearFilters = () => { | const clearFilters = () => { |
| <CloseButton onClick={closeCreateOfferModal}> | <CloseButton onClick={closeCreateOfferModal}> | ||||
| <CloseButtonIcon /> | <CloseButtonIcon /> | ||||
| </CloseButton> | </CloseButton> | ||||
| <Scroller> | |||||
| <Scroller isCarousel> | |||||
| {props?.offer?.offer?.images.map((image) => { | {props?.offer?.offer?.images.map((image) => { | ||||
| if (!image) return; | if (!image) return; | ||||
| return ( | return ( |
| search: newQueryString.toString(), | search: newQueryString.toString(), | ||||
| }); | }); | ||||
| } else { | } else { | ||||
| search.searchOffers(value); | |||||
| search.searchOffersImmediately(value); | |||||
| } | } | ||||
| }; | }; | ||||
| import React from "react"; | |||||
| import React, { useCallback } from "react"; | |||||
| import PropTypes from "prop-types"; | import PropTypes from "prop-types"; | ||||
| import { | import { | ||||
| EndIcon, | EndIcon, | ||||
| SearchInputContainer, | SearchInputContainer, | ||||
| } from "./SearchInput.styled"; | } from "./SearchInput.styled"; | ||||
| import { forwardRef } from "react"; | import { forwardRef } from "react"; | ||||
| import { useCallback } from "react"; | |||||
| // import { useCallback } from "react"; | |||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import { routeMatches } from "../../../util/helpers/routeHelpers"; | 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 SearchInput = forwardRef((props, ref) => { | ||||
| const { t } = useTranslation(); | 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( | const listener = useCallback( | ||||
| (event) => { | (event) => { | ||||
| if (event.keyCode === 13) { | if (event.keyCode === 13) { | ||||
| event.preventDefault(); | event.preventDefault(); | ||||
| props.handleSearch(ref.current.value); | |||||
| handleManualSearch(); | |||||
| } | } | ||||
| }, | }, | ||||
| [ref.current] | [ref.current] | ||||
| InputProps={{ | InputProps={{ | ||||
| endAdornment: ( | endAdornment: ( | ||||
| <EndIcon size="36px"> | <EndIcon size="36px"> | ||||
| <SearchIcon onClick={() => props.handleSearch(ref.current.value)} /> | |||||
| <SearchIcon onClick={handleManualSearch} /> | |||||
| </EndIcon> | </EndIcon> | ||||
| ), | ), | ||||
| }} | }} | ||||
| onChange={handleSearch} | |||||
| placeholder={t("header.searchOffers")} | placeholder={t("header.searchOffers")} | ||||
| onFocus={handleFocusSearch} | onFocus={handleFocusSearch} | ||||
| onBlur={handleBlurSearch} | onBlur={handleBlurSearch} |
| ListContainer, | ListContainer, | ||||
| } from "./HorizontalScroller.styled"; | } from "./HorizontalScroller.styled"; | ||||
| import { ArrowButton } from "../Buttons/ArrowButton/ArrowButton"; | import { ArrowButton } from "../Buttons/ArrowButton/ArrowButton"; | ||||
| import useIsMobile from "../../hooks/useIsMobile"; | |||||
| const HorizontalScroller = (props) => { | const HorizontalScroller = (props) => { | ||||
| const scrollRef = useRef(null); | const scrollRef = useRef(null); | ||||
| const { isMobile } = useIsMobile(); | |||||
| const [isDisabledLeftButton, setIsDisabledLeftButton] = useState(true); | const [isDisabledLeftButton, setIsDisabledLeftButton] = useState(true); | ||||
| const [isDisabledRightButton, setIsDisabledRightButton] = useState(false); | const [isDisabledRightButton, setIsDisabledRightButton] = useState(false); | ||||
| ) { | ) { | ||||
| if (isDisabledRightButton !== true) setIsDisabledRightButton(true); | 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 = () => { | const handleLeft = () => { | ||||
| if (scrollRef.current.scrollLeft - 50 <= 0) { | if (scrollRef.current.scrollLeft - 50 <= 0) { | ||||
| ) { | ) { | ||||
| if (isDisabledRightButton !== false) setIsDisabledRightButton(false); | 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 ( | return ( | ||||
| <HorizontalScrollerContainer | <HorizontalScrollerContainer | ||||
| containerStyle: PropTypes.any, | containerStyle: PropTypes.any, | ||||
| listStyle: PropTypes.any, | listStyle: PropTypes.any, | ||||
| hideArrows: PropTypes.bool, | hideArrows: PropTypes.bool, | ||||
| isCarousel: PropTypes.bool, | |||||
| }; | }; | ||||
| export default HorizontalScroller; | export default HorizontalScroller; |
| // Those hooks are below becouse function apply cannot be put on props before initialization | // Those hooks are below becouse function apply cannot be put on props before initialization | ||||
| const sorting = useSorting(apply); | const sorting = useSorting(apply); | ||||
| const paging = usePaging(apply); | const paging = usePaging(apply); | ||||
| const search = useSearch(apply); | |||||
| const search = useSearch(applyFilters); | |||||
| // On every change of search string, offers should be immediately searched | // On every change of search string, offers should be immediately searched | ||||
| useEffect(() => { | useEffect(() => { | ||||
| filters, | filters, | ||||
| sorting, | sorting, | ||||
| paging, | paging, | ||||
| search, | |||||
| queryStringHook, | queryStringHook, | ||||
| allOffersToShow, | allOffersToShow, | ||||
| totalOffers, | totalOffers, |
| setSearchStringLocally(searchValue); | setSearchStringLocally(searchValue); | ||||
| }; | }; | ||||
| const searchOffersImmediately = (searchValue) => { | |||||
| searchOffers(searchValue); | |||||
| applyAllFilters(); | |||||
| }; | |||||
| const clear = () => { | const clear = () => { | ||||
| setSearchStringLocally(""); | setSearchStringLocally(""); | ||||
| }; | }; | ||||
| return { | return { | ||||
| searchOffers, | searchOffers, | ||||
| setSearchStringLocally, | setSearchStringLocally, | ||||
| searchOffersImmediately, | |||||
| searchStringLocally, | searchStringLocally, | ||||
| searchString, | searchString, | ||||
| clear, | clear, |
| const formik = props.formik; | const formik = props.formik; | ||||
| return ( | return ( | ||||
| <> | <> | ||||
| {formik.errors?.location ? ( | |||||
| {formik.errors?.location && formik.touched?.location ? ( | |||||
| <ErrorMessageContainer>{formik.errors.location}</ErrorMessageContainer> | <ErrorMessageContainer>{formik.errors.location}</ErrorMessageContainer> | ||||
| ) : formik.errors?.phoneNumber ? ( | |||||
| ) : formik.errors?.phoneNumber && formik.touched?.phoneNumber ? ( | |||||
| <ErrorMessageContainer> | <ErrorMessageContainer> | ||||
| {formik.errors.phoneNumber} | {formik.errors.phoneNumber} | ||||
| </ErrorMessageContainer> | </ErrorMessageContainer> | ||||
| ) : formik.errors?.website ? ( | |||||
| ) : formik.errors?.website && formik.touched?.website ? ( | |||||
| <ErrorMessageContainer>{formik.errors.website}</ErrorMessageContainer> | <ErrorMessageContainer>{formik.errors.website}</ErrorMessageContainer> | ||||
| ) : ( | ) : ( | ||||
| <></> | <></> |
| initialValues: thirdPartInitialValues(props.informations), | initialValues: thirdPartInitialValues(props.informations), | ||||
| validationSchema: thirdPartValidation(locations), | validationSchema: thirdPartValidation(locations), | ||||
| onSubmit: handleSubmit, | onSubmit: handleSubmit, | ||||
| validateOnBlur: true, | |||||
| enableReinitialize: true, | enableReinitialize: true, | ||||
| }); | }); | ||||
| let debounceFn; | |||||
| export const debounceHelper = (fn, delay) => { | |||||
| if (debounceFn) clearTimeout(debounceFn); | |||||
| debounceFn = setTimeout(() => { | |||||
| fn(); | |||||
| }, delay); | |||||
| }; |
| export const replaceInRoute = (route, pathVariables = {}) => { | export const replaceInRoute = (route, pathVariables = {}) => { | ||||
| const keys = Object.keys(pathVariables); | const keys = Object.keys(pathVariables); | ||||
| return keys.reduce( | return keys.reduce( | ||||
| (acc, key) => acc.replace(`:${key}`, pathVariables[`${key}`]), | (acc, key) => acc.replace(`:${key}`, pathVariables[`${key}`]), | ||||
| route | route |
| i18next.t("register.labelLocationValid") | i18next.t("register.labelLocationValid") | ||||
| ), | ), | ||||
| website: Yup.string().matches( | 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") | |||||
| ), | ), | ||||
| }); | }); |