| @@ -24,6 +24,7 @@ | |||
| "lodash": "^4.17.21", | |||
| "lodash.isempty": "^4.4.0", | |||
| "owasp-password-strength-test": "^1.3.0", | |||
| "query-string": "^7.1.1", | |||
| "react": "^17.0.2", | |||
| "react-dom": "^17.0.2", | |||
| "react-helmet-async": "^1.0.9", | |||
| @@ -0,0 +1,13 @@ | |||
| <svg width="25" height="28" viewBox="0 0 25 28" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||
| <g clip-path="url(#clip0_86_8377)"> | |||
| <path d="M7.49117 6.3122L1.21472 7.6463L2.54882 13.9227L8.82527 12.5886L7.49117 6.3122Z" stroke="#9677BE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| <path d="M17.3542 4.21577L11.0778 5.54987L12.4119 11.8263L18.6883 10.4922L17.3542 4.21577Z" stroke="#9677BE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| <path d="M19.4506 14.0787L13.1742 15.4128L14.5083 21.6893L20.7847 20.3552L19.4506 14.0787Z" stroke="#9677BE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| <path d="M9.5876 16.1752L3.31116 17.5093L4.64526 23.7857L10.9217 22.4516L9.5876 16.1752Z" stroke="#9677BE" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| </g> | |||
| <defs> | |||
| <clipPath id="clip0_86_8377"> | |||
| <rect width="22" height="22" fill="white" transform="translate(-2.04688 5.52814) rotate(-12)"/> | |||
| </clipPath> | |||
| </defs> | |||
| </svg> | |||
| @@ -0,0 +1,6 @@ | |||
| <svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||
| <path d="M9.16667 2.75H2.75V9.16667H9.16667V2.75Z" stroke="#4D4D4D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| <path d="M19.2499 2.75H12.8333V9.16667H19.2499V2.75Z" stroke="#4D4D4D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| <path d="M19.2499 12.8333H12.8333V19.25H19.2499V12.8333Z" stroke="#4D4D4D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| <path d="M9.16667 12.8333H2.75V19.25H9.16667V12.8333Z" stroke="#4D4D4D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> | |||
| </svg> | |||
| @@ -0,0 +1,3 @@ | |||
| <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||
| <rect x="0.5" y="0.5" width="13" height="13" rx="6.5" fill="#64468B" stroke="#5A3984"/> | |||
| </svg> | |||
| @@ -0,0 +1,3 @@ | |||
| <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> | |||
| <rect x="0.5" y="0.5" width="13" height="13" rx="6.5" stroke="#5A3984"/> | |||
| </svg> | |||
| @@ -1,40 +1,151 @@ | |||
| import React from "react"; | |||
| import React, { useEffect } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| ContentContainer, | |||
| FilterCardContainer, | |||
| Footer, | |||
| Header, | |||
| Title, | |||
| } from "./FilterCard.styled"; | |||
| import { ReactComponent as Subcategory } from "../../../assets/images/svg/subcategory.svg"; | |||
| import { ReactComponent as Category } from "../../../assets/images/svg/category.svg"; | |||
| import { ReactComponent as CategoryChosen } from "../../../assets/images/svg/category-chosen.svg"; | |||
| import { ReactComponent as Location } from "../../../assets/images/svg/location.svg"; | |||
| import Link from "../../Link/Link"; | |||
| import { PrimaryButton } from "../../Buttons/PrimaryButton/PrimaryButton"; | |||
| import FilterDropdown from "./FilterDropdown.js/FilterDropdown"; | |||
| import FilterCheckboxDropdown from "./FilterDropdown/Checkbox/FilterCheckboxDropdown"; | |||
| import Mockupdata from "./Mockupdata"; | |||
| import { useState } from "react"; | |||
| import FilterRadioDropdown from "./FilterDropdown/Radio/FilterRadioDropdown"; | |||
| import { useHistory } from "react-router-dom"; | |||
| import { HOME_PAGE } from "../../../constants/pages"; | |||
| import qs from "query-string"; | |||
| import { useTranslation } from "react-i18next"; | |||
| const FilterCard = () => { | |||
| const [appliedFilters, setAppliedFilters] = useState([]); | |||
| const [selectedCategory, setSelectedCategory] = useState(0); | |||
| const [selectedSubcategory, setSelectedSubcategory] = useState(0); | |||
| const history = useHistory(); | |||
| const {t} = useTranslation(); | |||
| useEffect(() => { | |||
| const queryString = history.location.search.substring(1); | |||
| const queryObject = qs.parse(queryString); | |||
| if (queryObject.category) { | |||
| setSelectedCategory( | |||
| Mockupdata[1].find( | |||
| (item) => item.string === queryObject.category.toString() | |||
| ).id | |||
| ); | |||
| } | |||
| if (queryObject.subcategory) { | |||
| setSelectedSubcategory( | |||
| Mockupdata[1].find( | |||
| (item) => item.string === queryObject.subcategory.toString() | |||
| ).id | |||
| ); | |||
| } | |||
| if (queryObject.city) { | |||
| let filters = []; | |||
| queryObject.city.forEach((item) => { | |||
| filters.push(Mockupdata[0].find((p) => p.string === item).id); | |||
| }); | |||
| setAppliedFilters([...filters]); | |||
| } | |||
| }, []); | |||
| const handleFilters = () => { | |||
| let queryObject = {}; | |||
| if (selectedCategory !== 0) { | |||
| queryObject = { | |||
| category: Mockupdata[1].find( | |||
| (item) => item.id.toString() === selectedCategory.toString() | |||
| ).string, | |||
| }; | |||
| if (selectedSubcategory !== 0) { | |||
| queryObject = { | |||
| ...queryObject, | |||
| subcategory: Mockupdata[1].find( | |||
| (item) => item.id.toString() === selectedSubcategory.toString() | |||
| ).string, | |||
| }; | |||
| } | |||
| } | |||
| if (appliedFilters.length > 0) { | |||
| let arrayObject = []; | |||
| appliedFilters.forEach((item) => { | |||
| arrayObject.push( | |||
| Mockupdata[0].find((p) => p.id.toString() === item.toString()).string | |||
| ); | |||
| }); | |||
| queryObject = { ...queryObject, city: arrayObject }; | |||
| } | |||
| const queryString = qs.stringify(queryObject); | |||
| history.push({ | |||
| pathname: HOME_PAGE, | |||
| search: "?" + queryString, | |||
| }); | |||
| }; | |||
| const clearFilters = () => { | |||
| setAppliedFilters([]); | |||
| setSelectedCategory(0); | |||
| setSelectedSubcategory(0); | |||
| }; | |||
| return ( | |||
| <FilterCardContainer> | |||
| <Header> | |||
| <Title>Filteri</Title> | |||
| <Link to="#" textsize={"12px"}> | |||
| Ponisti filtere | |||
| </Link> | |||
| </Header> | |||
| <ContentContainer> | |||
| <Header> | |||
| <Title>{t("filters.title")}</Title> | |||
| <Link to="#" textsize={"12px"} onClick={clearFilters}> | |||
| {t("filters.cancel")} | |||
| </Link> | |||
| </Header> | |||
| <FilterRadioDropdown | |||
| data={[...Mockupdata[1]]} | |||
| icon={ | |||
| selectedCategory && selectedCategory !== 0 ? ( | |||
| <CategoryChosen /> | |||
| ) : ( | |||
| <Category /> | |||
| ) | |||
| } | |||
| title={ | |||
| selectedCategory && selectedCategory !== 0 | |||
| ? Mockupdata[1].find( | |||
| (item) => item.id.toString() === selectedCategory.toString() | |||
| ).string | |||
| : t("filters.categories.title") | |||
| } | |||
| searchPlaceholder={t("filters.categories.placeholder")} | |||
| setSelected={setSelectedCategory} | |||
| selected={selectedCategory} | |||
| /> | |||
| <FilterDropdown data={[]} icon={<Subcategory />} title="Podkategorija" /> | |||
| <FilterRadioDropdown | |||
| data={[...Mockupdata[1]]} | |||
| icon={<Subcategory />} | |||
| title={t("filters.subcategories.title")} | |||
| searchPlaceholder={t("filters.subcategories.placeholder")} | |||
| setSelected={setSelectedSubcategory} | |||
| selected={selectedSubcategory} | |||
| /> | |||
| <FilterDropdown | |||
| data={[...Mockupdata]} | |||
| icon={<Location />} | |||
| title="Lokacija" | |||
| /> | |||
| <FilterCheckboxDropdown | |||
| searchPlaceholder={t("filters.location.placeholder")} | |||
| data={[...Mockupdata[0]]} | |||
| filters={appliedFilters} | |||
| icon={<Location />} | |||
| title={t("filters.location.title")} | |||
| setItemsSelected={setAppliedFilters} | |||
| /> | |||
| </ContentContainer> | |||
| <Footer> | |||
| <PrimaryButton variant="outlined" fullWidth> | |||
| PRIMENI FILTERE | |||
| <PrimaryButton variant="outlined" fullWidth onClick={handleFilters}> | |||
| {t("filters.usefilters")} | |||
| </PrimaryButton> | |||
| </Footer> | |||
| </FilterCardContainer> | |||
| @@ -11,6 +11,9 @@ export const FilterCardContainer = styled(Box)` | |||
| width: 100%; | |||
| position: "fixed"; | |||
| left: 0; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| `; | |||
| export const Title = styled(Typography)` | |||
| font-size: 24px; | |||
| @@ -30,7 +33,17 @@ export const Header = styled(Box)` | |||
| export const Footer = styled(Box)` | |||
| position: absolute; | |||
| position: "sticky"; | |||
| bottom: 0; | |||
| & div button { | |||
| height: 48px; | |||
| padding-top: 7px; | |||
| } | |||
| & div button:hover { | |||
| background-color: ${selectedTheme.primaryPurple} !important; | |||
| color: ${selectedTheme.primaryBackgroundColor} !important; | |||
| } | |||
| `; | |||
| export const ContentContainer = styled(Box)` | |||
| ` | |||
| @@ -1,138 +0,0 @@ | |||
| import React, { useEffect, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import DropdownList from "../../../Dropdown/DropdownList/DropdownList"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import IconWithNumber from "../../../Icon/IconWithNumber/IconWithNumber"; | |||
| import { ReactComponent as DropdownDown } from "../../../../assets/images/svg/dropdownDown.svg"; | |||
| import { ReactComponent as DropdownUp } from "../../../../assets/images/svg/dropdownUp.svg"; | |||
| import { ReactComponent as Close } from "../../../../assets/images/svg/closeWhite.svg"; | |||
| import { ReactComponent as CloseBlack } from "../../../../assets/images/svg/closeBlack.svg"; | |||
| import { | |||
| ClearText, | |||
| SelectedItem, | |||
| SelectedItemsContainer, | |||
| } from "./FilterDropdown.styled"; | |||
| import { TextField } from "../../../TextFields/TextField/TextField"; | |||
| import DropdownItem from "../../../Dropdown/DropdownItem/DropdownItem"; | |||
| import { CheckBox } from "../../../CheckBox/CheckBox"; | |||
| const FilterDropdown = (props) => { | |||
| const [filtersApplied, setFiltersApplied] = useState([]); | |||
| const [toSearch, setToSearch] = useState(""); | |||
| const [dataToShow, setDataToShow] = useState([]); | |||
| ///IMPLEMENTIRATI PROP FILTERS KOJI MENJA LOKALNO STANJE OVE KOMPONENTE U ZAVISNOSTI OD NJEGOVE PROMENE | |||
| ///ZA FUNKCIJU PONISTI FILTERE | |||
| const {data} = props; | |||
| useEffect(() => { | |||
| setDataToShow([...data]) | |||
| }, []); | |||
| useEffect(() => { | |||
| if (toSearch.length > 0) { | |||
| setDataToShow(data.filter(item => item.string.toLowerCase().includes(toSearch.toLowerCase()))); | |||
| } else { | |||
| setDataToShow([...data]); | |||
| } | |||
| }, [toSearch]) | |||
| const handleChange = (item) => { | |||
| if (filtersApplied.find((p) => p.id === item.id)) { | |||
| setFiltersApplied([...filtersApplied.filter((p) => p.id !== item.id)]); | |||
| } else { | |||
| setFiltersApplied([...filtersApplied, item]); | |||
| } | |||
| let indexOfItem = dataToShow.findIndex((p) => p.id === item.id); | |||
| let items = [...dataToShow]; | |||
| items[indexOfItem].checked = !item.checked; | |||
| setDataToShow([...items]); | |||
| }; | |||
| const handleDelete = (item) => { | |||
| setFiltersApplied([...filtersApplied.filter((p) => p.id !== item.id)]); | |||
| let indexOfItem = dataToShow.findIndex((p) => p.id === item.id); | |||
| let items = [...dataToShow]; | |||
| items[indexOfItem].checked = false; | |||
| setDataToShow([...items]); | |||
| }; | |||
| const handleClear = () => { | |||
| setToSearch(""); | |||
| }; | |||
| return ( | |||
| <DropdownList | |||
| title={props.title} | |||
| textcolor={ | |||
| filtersApplied.length > 0 | |||
| ? selectedTheme.primaryPurple | |||
| : selectedTheme.primaryText | |||
| } | |||
| dropdownIcon={ | |||
| <IconWithNumber number={filtersApplied.length}> | |||
| {props.icon} | |||
| </IconWithNumber> | |||
| } | |||
| toggleIconClosed={<DropdownDown />} | |||
| toggleIconOpened={<DropdownUp />} | |||
| fullWidth | |||
| toggleIconStyles={{ | |||
| backgroundColor: selectedTheme.primaryIconBackgroundColor, | |||
| }} | |||
| headerOptions={ | |||
| <React.Fragment> | |||
| <SelectedItemsContainer> | |||
| {filtersApplied.map((item) => ( | |||
| <SelectedItem key={item.id} onClick={() => handleDelete(item)}> | |||
| {item.string}{" "} | |||
| <Close style={{ position: "relative", top: "3px" }} /> | |||
| </SelectedItem> | |||
| ))} | |||
| </SelectedItemsContainer> | |||
| <TextField | |||
| placeholder={"Pretrazite lokacije..."} | |||
| italicPlaceholder | |||
| value={toSearch} | |||
| onChange={(event) => setToSearch(event.target.value)} | |||
| textsize={"12px"} | |||
| font={"Open Sans"} | |||
| fullWidth | |||
| height={"40px"} | |||
| containerStyle={{ marginTop: "6px" }} | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <ClearText onClick={handleClear}> | |||
| <CloseBlack /> | |||
| </ClearText> | |||
| ), | |||
| }} | |||
| /> | |||
| </React.Fragment> | |||
| } | |||
| > | |||
| {dataToShow.map((item) => ( | |||
| <DropdownItem key={item.id}> | |||
| <CheckBox | |||
| leftText={item.string} | |||
| rightText={item.numberOfProducts} | |||
| value={item.id} | |||
| checked={item.checked} | |||
| onChange={() => handleChange(item)} | |||
| fullWidth | |||
| /> | |||
| </DropdownItem> | |||
| ))} | |||
| </DropdownList> | |||
| ); | |||
| }; | |||
| FilterDropdown.propTypes = { | |||
| children: PropTypes.node, | |||
| icon: PropTypes.node, | |||
| data: PropTypes.array, | |||
| title: PropTypes.string, | |||
| }; | |||
| export default FilterDropdown; | |||
| @@ -0,0 +1,144 @@ | |||
| import React, { useEffect, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import DropdownList from "../../../../Dropdown/DropdownList/DropdownList"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| import IconWithNumber from "../../../../Icon/IconWithNumber/IconWithNumber"; | |||
| import { ReactComponent as DropdownDown } from "../../../../../assets/images/svg/dropdownDown.svg"; | |||
| import { ReactComponent as DropdownUp } from "../../../../../assets/images/svg/dropdownUp.svg"; | |||
| import { ReactComponent as Close } from "../../../../../assets/images/svg/closeWhite.svg"; | |||
| import { ReactComponent as CloseBlack } from "../../../../../assets/images/svg/closeBlack.svg"; | |||
| import { | |||
| ClearText, | |||
| SelectedItem, | |||
| SelectedItemsContainer, | |||
| } from "./FilterCheckboxDropdown.styled"; | |||
| import { TextField } from "../../../../TextFields/TextField/TextField"; | |||
| import DropdownItem from "../../../../Dropdown/DropdownItem/DropdownItem"; | |||
| import { CheckBox } from "../../../../CheckBox/CheckBox"; | |||
| const FilterCheckboxDropdown = (props) => { | |||
| const [toSearch, setToSearch] = useState(""); | |||
| const [dataToShow, setDataToShow] = useState([]); | |||
| const { data } = props; | |||
| useEffect(() => { | |||
| setDataToShow([...data]); | |||
| }, []); | |||
| useEffect(() => { | |||
| if (toSearch.length > 0) { | |||
| setDataToShow( | |||
| data.filter((item) => | |||
| item.string.toLowerCase().includes(toSearch.toLowerCase()) | |||
| ) | |||
| ); | |||
| } else { | |||
| setDataToShow([...data]); | |||
| } | |||
| }, [toSearch]); | |||
| const handleChange = (item) => { | |||
| if (props.oneValueAllowed) { | |||
| props.setItemsSelected[item.id]; | |||
| } else { | |||
| if (props.filters.find((p) => p.id === item.id)) { | |||
| props.setItemsSelected((itemsSelected) => [ | |||
| ...itemsSelected.filter((p) => p !== item.id), | |||
| ]); | |||
| } else { | |||
| props.setItemsSelected((itemsSelected) => [...itemsSelected, item.id]); | |||
| } | |||
| } | |||
| }; | |||
| const handleDelete = (item) => { | |||
| props.setItemsSelected([...props.filters.filter((p) => p !== item)]); | |||
| }; | |||
| const handleClear = () => { | |||
| setToSearch(""); | |||
| }; | |||
| return ( | |||
| <DropdownList | |||
| title={props.title} | |||
| textcolor={ | |||
| props.filters.length > 0 | |||
| ? selectedTheme.primaryPurple | |||
| : selectedTheme.primaryText | |||
| } | |||
| dropdownIcon={ | |||
| <IconWithNumber number={props.filters.length}> | |||
| {props.icon} | |||
| </IconWithNumber> | |||
| } | |||
| toggleIconClosed={<DropdownDown />} | |||
| toggleIconOpened={<DropdownUp />} | |||
| fullWidth | |||
| toggleIconStyles={{ | |||
| backgroundColor: selectedTheme.primaryIconBackgroundColor, | |||
| }} | |||
| headerOptions={ | |||
| <React.Fragment> | |||
| <SelectedItemsContainer> | |||
| {props.filters.map((item) => ( | |||
| <SelectedItem key={item} onClick={() => handleDelete(item)}> | |||
| {data.find((p) => p.id === item).string} | |||
| <Close style={{ position: "relative", top: "3px" }} /> | |||
| </SelectedItem> | |||
| ))} | |||
| </SelectedItemsContainer> | |||
| <TextField | |||
| placeholder={props.searchPlaceholder} | |||
| italicPlaceholder | |||
| value={toSearch} | |||
| onChange={(event) => setToSearch(event.target.value)} | |||
| textsize={"12px"} | |||
| font={"Open Sans"} | |||
| fullWidth | |||
| height={"40px"} | |||
| containerStyle={{ marginTop: "6px" }} | |||
| InputProps={{ | |||
| endAdornment: | |||
| toSearch.length > 0 ? ( | |||
| <ClearText onClick={handleClear}> | |||
| <CloseBlack /> | |||
| </ClearText> | |||
| ) : ( | |||
| <React.Fragment /> | |||
| ), | |||
| }} | |||
| /> | |||
| </React.Fragment> | |||
| } | |||
| > | |||
| {dataToShow.map((item) => ( | |||
| <DropdownItem key={item.id}> | |||
| <CheckBox | |||
| leftText={item.string} | |||
| rightText={item.numberOfProducts} | |||
| value={item.id} | |||
| checked={props.filters.includes(item.id, 0)} | |||
| onChange={() => handleChange(item)} | |||
| fullWidth | |||
| /> | |||
| </DropdownItem> | |||
| ))} | |||
| </DropdownList> | |||
| ); | |||
| }; | |||
| FilterCheckboxDropdown.propTypes = { | |||
| children: PropTypes.node, | |||
| icon: PropTypes.node, | |||
| data: PropTypes.array, | |||
| title: PropTypes.string, | |||
| oneValueAllowed: PropTypes.bool, | |||
| searchPlaceholder: PropTypes.string, | |||
| setItemsSelected: PropTypes.func, | |||
| filters: PropTypes.array, | |||
| }; | |||
| FilterCheckboxDropdown.defaultProps = { | |||
| oneValueAllowed: false, | |||
| }; | |||
| export default FilterCheckboxDropdown; | |||
| @@ -1,6 +1,6 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../themes"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| export const SelectedItemsContainer = styled(Box)` | |||
| display: flex; | |||
| @@ -24,15 +24,15 @@ export const SelectedItem = styled(Box)` | |||
| height: 22px; | |||
| `; | |||
| export const ClearText = styled(Box)` | |||
| padding-top: 1px; | |||
| border-radius: 100%; | |||
| cursor: pointer; | |||
| padding-right: 2px; | |||
| position: relative; | |||
| left: 6px; | |||
| width: 21px; | |||
| height: 21px; | |||
| &:hover { | |||
| background-color: ${selectedTheme.primaryIconBackgroundColor}; | |||
| } | |||
| ` | |||
| padding-top: 1px; | |||
| border-radius: 100%; | |||
| cursor: pointer; | |||
| padding-right: 2px; | |||
| position: relative; | |||
| left: 6px; | |||
| width: 21px; | |||
| height: 21px; | |||
| &:hover { | |||
| background-color: ${selectedTheme.primaryIconBackgroundColor}; | |||
| } | |||
| `; | |||
| @@ -0,0 +1,117 @@ | |||
| import React, { useEffect, useState } from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import DropdownList from "../../../../Dropdown/DropdownList/DropdownList"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| import { ReactComponent as DropdownDown } from "../../../../../assets/images/svg/dropdownDown.svg"; | |||
| import { ReactComponent as DropdownUp } from "../../../../../assets/images/svg/dropdownUp.svg"; | |||
| import { ReactComponent as CloseBlack } from "../../../../../assets/images/svg/closeBlack.svg"; | |||
| import { ClearText } from "./FilterRadioDropdown.styled"; | |||
| import { TextField } from "../../../../TextFields/TextField/TextField"; | |||
| import DropdownItem from "../../../../Dropdown/DropdownItem/DropdownItem"; | |||
| import RadioButton from "../../../../Radio/Button/RadioButton"; | |||
| import RadioGroup from "../../../../Radio/Group/RadioGroup"; | |||
| const FilterRadioDropdown = (props) => { | |||
| const [toSearch, setToSearch] = useState(""); | |||
| const [dataToShow, setDataToShow] = useState([]); | |||
| const { data } = props; | |||
| useEffect(() => { | |||
| setDataToShow([...data]); | |||
| }, []); | |||
| useEffect(() => { | |||
| if (toSearch.length > 0) { | |||
| setDataToShow( | |||
| data.filter((item) => | |||
| item.string.toLowerCase().includes(toSearch.toLowerCase()) | |||
| ) | |||
| ); | |||
| } else { | |||
| setDataToShow([...data]); | |||
| } | |||
| }, [toSearch]); | |||
| const handleClear = () => { | |||
| setToSearch(""); | |||
| }; | |||
| const handleChange = (value) => { | |||
| props.setSelected(value); | |||
| }; | |||
| return ( | |||
| <DropdownList | |||
| title={props.title} | |||
| textcolor={ | |||
| props.selected !== 0 | |||
| ? selectedTheme.primaryPurple | |||
| : selectedTheme.primaryText | |||
| } | |||
| dropdownIcon={props.icon} | |||
| toggleIconClosed={<DropdownDown />} | |||
| toggleIconOpened={<DropdownUp />} | |||
| fullWidth | |||
| toggleIconStyles={{ | |||
| backgroundColor: selectedTheme.primaryIconBackgroundColor, | |||
| }} | |||
| headerOptions={ | |||
| <React.Fragment> | |||
| <TextField | |||
| placeholder={props.searchPlaceholder} | |||
| italicPlaceholder | |||
| value={toSearch} | |||
| onChange={(event) => setToSearch(event.target.value)} | |||
| textsize={"12px"} | |||
| font={"Open Sans"} | |||
| fullWidth | |||
| height={"40px"} | |||
| containerStyle={{ marginTop: "6px" }} | |||
| InputProps={{ | |||
| endAdornment: | |||
| toSearch.length > 0 ? ( | |||
| <ClearText onClick={handleClear}> | |||
| <CloseBlack /> | |||
| </ClearText> | |||
| ) : ( | |||
| <React.Fragment /> | |||
| ), | |||
| }} | |||
| /> | |||
| </React.Fragment> | |||
| } | |||
| > | |||
| <RadioGroup onChange={(event) => handleChange(event.target.value)}> | |||
| {dataToShow.map((item) => ( | |||
| <DropdownItem key={item.id}> | |||
| <RadioButton | |||
| value={item.id} | |||
| label={item.string} | |||
| number={item.numberOfProducts} | |||
| fullWidth | |||
| checked={props.selected.toString() === item.id.toString()} | |||
| onChange={handleChange} | |||
| /> | |||
| </DropdownItem> | |||
| ))} | |||
| </RadioGroup> | |||
| </DropdownList> | |||
| ); | |||
| }; | |||
| FilterRadioDropdown.propTypes = { | |||
| children: PropTypes.node, | |||
| icon: PropTypes.node, | |||
| data: PropTypes.array, | |||
| title: PropTypes.string, | |||
| oneValueAllowed: PropTypes.bool, | |||
| fullWidth: PropTypes.bool, | |||
| searchPlaceholder: PropTypes.string, | |||
| setSelected: PropTypes.func, | |||
| selected: PropTypes.number, | |||
| }; | |||
| FilterRadioDropdown.defaultProps = { | |||
| oneValueAllowed: false, | |||
| fullWidth: false, | |||
| }; | |||
| export default FilterRadioDropdown; | |||
| @@ -0,0 +1,24 @@ | |||
| import { Box } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../../../themes"; | |||
| export const SelectedItemsContainer = styled(Box)` | |||
| display: flex; | |||
| flex-direction: row; | |||
| flex-wrap: wrap; | |||
| margin-top: 5px; | |||
| `; | |||
| export const ClearText = styled(Box)` | |||
| padding-top: 1px; | |||
| border-radius: 100%; | |||
| cursor: pointer; | |||
| padding-right: 2px; | |||
| position: relative; | |||
| left: 6px; | |||
| width: 21px; | |||
| height: 21px; | |||
| &:hover { | |||
| background-color: ${selectedTheme.primaryIconBackgroundColor}; | |||
| } | |||
| ` | |||
| @@ -1,4 +1,4 @@ | |||
| export default [{ | |||
| export default [[{ | |||
| string: "Beograd", | |||
| numberOfProducts: 17, | |||
| id: 0, | |||
| @@ -33,4 +33,36 @@ export default [{ | |||
| numberOfProducts: 23, | |||
| id: 5, | |||
| checked: false | |||
| }]; | |||
| }], | |||
| [ | |||
| { | |||
| string: "SVE KATEGORIJE", | |||
| numberOfProducts: 259, | |||
| id: 0, | |||
| }, | |||
| { | |||
| string: "Kategorija 1", | |||
| numberOfProducts: 46, | |||
| id: 1, | |||
| }, | |||
| { | |||
| string: "Kategorija 2", | |||
| numberOfProducts: 26, | |||
| id: 2, | |||
| }, | |||
| { | |||
| string: "Kategorija 3", | |||
| numberOfProducts: 91, | |||
| id: 3, | |||
| }, | |||
| { | |||
| string: "Kategorija 4", | |||
| numberOfProducts: 23, | |||
| id: 4, | |||
| }, | |||
| { | |||
| string: "Kategorija 5", | |||
| numberOfProducts: 20, | |||
| id: 5, | |||
| }, | |||
| ]]; | |||
| @@ -52,7 +52,7 @@ CheckBox.propTypes = { | |||
| color: PropTypes.string, | |||
| name: PropTypes.string, | |||
| leftText: PropTypes.string, | |||
| rightText: PropTypes.string, | |||
| rightText: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), | |||
| maxWidth: PropTypes.string, | |||
| checked: PropTypes.bool, | |||
| value: PropTypes.number, | |||
| @@ -16,8 +16,6 @@ export const CheckBoxStyled = styled(Checkbox)` | |||
| padding: 6px; | |||
| `; | |||
| export const FormControlLabelStyled = styled(FormControlLabel)` | |||
| font-family: "Open Sans"; | |||
| font-size: 12px; | |||
| ${(props) => | |||
| props.fullWidth && | |||
| ` | |||
| @@ -26,6 +24,14 @@ export const FormControlLabelStyled = styled(FormControlLabel)` | |||
| flex: 1; | |||
| `} | |||
| margin-right: 0; | |||
| & label { | |||
| font-family: "Open Sans"; | |||
| font-size: 12px; | |||
| } | |||
| & span:nth-child(1) svg { | |||
| width: 18px; | |||
| height: 18px; | |||
| } | |||
| & span:nth-child(2) { | |||
| flex: 1; | |||
| } | |||
| @@ -14,6 +14,6 @@ export const Label = (props) => { | |||
| Label.propTypes = { | |||
| onClick: PropTypes.func, | |||
| leftText: PropTypes.string, | |||
| rightText: PropTypes.string, | |||
| rightText: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), | |||
| maxWidth: PropTypes.string, | |||
| }; | |||
| @@ -10,6 +10,7 @@ export const LabelContainer = styled(Box)` | |||
| ` | |||
| export const LeftLabel = styled(FormLabel)` | |||
| font-family: "Open Sans"; | |||
| overflow: hidden; | |||
| text-overflow: ellipsis; | |||
| white-space: nowrap; | |||
| @@ -26,5 +27,6 @@ export const RightLabel = styled(FormLabel)` | |||
| max-width: 100px; | |||
| cursor: pointer; | |||
| color: ${selectedTheme.primaryText}; | |||
| font-family: "Open Sans"; | |||
| ` | |||
| @@ -18,9 +18,14 @@ const DropdownList = (props) => { | |||
| <DropdownListContainer fullWidth={props.fullWidth}> | |||
| <DropdownHeader> | |||
| {props.dropdownIcon && ( | |||
| <DropdownIcon>{props.dropdownIcon}</DropdownIcon> | |||
| <DropdownIcon onClick={() => setListShown((prevState) => !prevState)}> | |||
| {props.dropdownIcon} | |||
| </DropdownIcon> | |||
| )} | |||
| <DropdownTitle onClick={() => setListShown((prevState) => !prevState)} textcolor={props.textcolor}> | |||
| <DropdownTitle | |||
| onClick={() => setListShown((prevState) => !prevState)} | |||
| textcolor={props.textcolor} | |||
| > | |||
| {props.title} | |||
| </DropdownTitle> | |||
| {listShown ? ( | |||
| @@ -6,7 +6,7 @@ import { IconButton } from "../../Buttons/IconButton/IconButton"; | |||
| export const DropdownListContainer = styled(Box)` | |||
| width: ${(props) => | |||
| props.fullWidth ? "100%" : props.width ? props.width : "250px"}; | |||
| padding: 8px; | |||
| padding: 8px 0; | |||
| `; | |||
| export const DropdownTitle = styled(Typography)` | |||
| @@ -45,6 +45,7 @@ export const DropdownIcon = styled(IconButton)` | |||
| export const ListContainer = styled(Box)` | |||
| padding-left: 15px; | |||
| margin-left: 15px; | |||
| `; | |||
| export const DropdownHeader = styled(Box)` | |||
| @@ -52,7 +53,6 @@ export const DropdownHeader = styled(Box)` | |||
| flex-direction: row; | |||
| `; | |||
| export const DropdownOptions = styled(Box)` | |||
| padding-left: 7px; | |||
| `; | |||
| export const ToggleContainer = styled(Box)` | |||
| display: ${(props) => (props.shouldShow ? "block" : "none")}; | |||
| @@ -7,11 +7,11 @@ const MainLayout = (props) => { | |||
| return ( | |||
| <MainLayoutContainer maxWidth={true}> | |||
| {props.children} | |||
| <Grid container xs={12} maxHeight="lg"> | |||
| <LeftCard item xs={3}> | |||
| <Grid container lg={12} maxHeight="lg"> | |||
| <LeftCard item xs={2} lg={3} xl={2.4} md={4}> | |||
| {props.leftCard} | |||
| </LeftCard> | |||
| <Content item xs={8}> | |||
| <Content item xs={10} lg={9} xl={9.6} md={8}> | |||
| {props.content} | |||
| </Content> | |||
| </Grid> | |||
| @@ -4,7 +4,7 @@ import { LinkStyled } from "./Link.styled"; | |||
| const Link = (props) => { | |||
| return ( | |||
| <LinkStyled {...props} href={props.href}> | |||
| <LinkStyled {...props} href={props.href} onClick={props.onClick}> | |||
| {props.children} | |||
| </LinkStyled> | |||
| ); | |||
| @@ -17,6 +17,7 @@ Link.propTypes = { | |||
| align: PropTypes.oneOf(["left", "right", "center"]), | |||
| textsize: PropTypes.string, | |||
| lineheight: PropTypes.string, | |||
| onClick: PropTypes.func, | |||
| }; | |||
| Link.defaultProps = { | |||
| font: "Poppins", | |||
| @@ -0,0 +1,52 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { | |||
| FormControlLabelStyled, | |||
| RadioButtonContainer, | |||
| RadioButtonStyled, | |||
| } from "./RadioButton.styled"; | |||
| import { ReactComponent as RadioChecked } from "../../../assets/images/svg/radio-checked.svg"; | |||
| import { ReactComponent as RadioUnchecked } from "../../../assets/images/svg/radio-unchecked.svg"; | |||
| import { Label } from "../../CheckBox/Label"; | |||
| const RadioButton = (props) => { | |||
| return ( | |||
| <RadioButtonContainer fullWidth={props.fullWidth}> | |||
| <FormControlLabelStyled | |||
| value={props.value} | |||
| fullWidth={props.fullWidth} | |||
| control={ | |||
| <RadioButtonStyled | |||
| icon={<RadioUnchecked />} | |||
| onChange={() => props.onChange(props.value)} | |||
| checkedIcon={<RadioChecked />} | |||
| checked={props.checked} | |||
| /> | |||
| } | |||
| label={ | |||
| <Label | |||
| leftText={props.label} | |||
| rightText={props.number} | |||
| onClick={() => props.onChange(props.value)} | |||
| /> | |||
| } | |||
| /> | |||
| </RadioButtonContainer> | |||
| ); | |||
| }; | |||
| RadioButton.propTypes = { | |||
| children: PropTypes.node, | |||
| value: PropTypes.number, | |||
| label: PropTypes.string, | |||
| number: PropTypes.number, | |||
| fullWidth: PropTypes.bool, | |||
| checked: PropTypes.bool, | |||
| onChange: PropTypes.func, | |||
| }; | |||
| RadioButton.defaultProps = { | |||
| fullWidth: false, | |||
| }; | |||
| export default RadioButton; | |||
| @@ -0,0 +1,40 @@ | |||
| import { Box, FormControlLabel, Radio } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| export const RadioButtonContainer = styled(Box)` | |||
| ${(props) => | |||
| props.fullWidth && | |||
| ` | |||
| width: 100%; | |||
| display: flex; | |||
| flex: 1; | |||
| `} | |||
| `; | |||
| export const RadioButtonStyled = styled(Radio)` | |||
| margin-top: 5px; | |||
| margin-bottom: 5px; | |||
| margin-right: 9px; | |||
| width: 14px; | |||
| height: 14px; | |||
| `; | |||
| export const FormControlLabelStyled = styled(FormControlLabel)` | |||
| ${(props) => | |||
| props.fullWidth && | |||
| ` | |||
| width: 100%; | |||
| display: flex; | |||
| flex: 1; | |||
| `} | |||
| margin-right: 0; | |||
| & label { | |||
| font-family: "Open Sans"; | |||
| font-size: 12px; | |||
| } | |||
| & span:nth-child(1) svg { | |||
| width: 16px; | |||
| height: 16px; | |||
| } | |||
| & span:nth-child(2) { | |||
| flex: 1; | |||
| } | |||
| `; | |||
| @@ -0,0 +1,20 @@ | |||
| import React from 'react' | |||
| import PropTypes from 'prop-types' | |||
| import { RadioGroupContainer } from './RadioGroup.styled' | |||
| const RadioGroup = (props) => { | |||
| return ( | |||
| <RadioGroupContainer onChange={props.onChange} value={props.value}> | |||
| {props.children} | |||
| </RadioGroupContainer> | |||
| ) | |||
| } | |||
| RadioGroup.propTypes = { | |||
| children: PropTypes.node, | |||
| onChange: PropTypes.func, | |||
| value: PropTypes.any, | |||
| defaultValue: PropTypes.any, | |||
| } | |||
| export default RadioGroup | |||
| @@ -0,0 +1,7 @@ | |||
| import { RadioGroup } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| export const RadioGroupContainer = styled(RadioGroup)` | |||
| padding-left: 5px; | |||
| width: 100%; | |||
| ` | |||
| @@ -103,4 +103,21 @@ export default { | |||
| label: 'Obnovite lozinku', | |||
| }, | |||
| }, | |||
| filters: { | |||
| title: "Filteri", | |||
| cancel: "Poništi filtere", | |||
| usefilters: 'Primeni filtere', | |||
| categories: { | |||
| title: "Kategorija", | |||
| placeholder: 'Pretraži kategorije...' | |||
| }, | |||
| subcategories: { | |||
| title: "Podkategorija", | |||
| placeholder: "Pretraži podkategorije..." | |||
| }, | |||
| location: { | |||
| title: "Lokacija", | |||
| placeholder: "Pretraži gradove..." | |||
| } | |||
| } | |||
| } | |||
| @@ -16,7 +16,7 @@ const HomePage = () => { | |||
| return ( | |||
| <HomePageContainer maxWidth={true}> | |||
| <Navbar /> | |||
| <MainLayout leftCard={<FilterCard />} /> | |||
| <MainLayout leftCard={<FilterCard />} content={<div></div>}/> | |||
| {/* <Box sx={{ mt: 4, mx: 4 }}> | |||
| <GridStyled container justifyContent="space-between"> | |||
| <GridStyled item xs={12} md={3}> | |||