-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #30 from web-Nearest-car-wash/feature/popupWithFil…
…ters Feature/popupWithFilters
- Loading branch information
Showing
21 changed files
with
599 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import { useEffect, useState } from 'react'; | ||
import { useDispatch, useSelector } from 'react-redux'; | ||
import styles from './PopupWithFilters.module.css'; | ||
import stylesButton from '../UI/ServiceButton/ServiceButton.module.css'; | ||
import FilterWithCheckbox from '../UI/FilterWithCheckbox/FilterWithCheckbox'; | ||
import FilterWithServices from '../UI/FilterWithServices/FilterWithServices'; | ||
import RemoveSearch from '../UI/icons/RemoveSearch'; | ||
import ClearFilters from '../UI/icons/ClearFilters'; | ||
import { | ||
fetchListServices, | ||
fetchListTypes, | ||
selectFilters, | ||
handleOpen, | ||
} from '../../store/filters/filters-slice'; | ||
import { fetchListFilteredCarWashes } from '../../store/carWashes/carWashes-slice'; | ||
|
||
function PopupWithFilters() { | ||
const dispatch = useDispatch(); | ||
const { listServices, listTypes, opened } = useSelector(selectFilters); | ||
const [checkedOpened, setCheckedOpened] = useState(false); | ||
const [checkedAroundClock, setCheckedAroundClock] = useState(false); | ||
const [checkedRaiting, setCheckedRaiting] = useState(false); | ||
const [arrServiceButtons, setArrServiceButtons] = useState([]); | ||
const [arrFilters, setArrFilters] = useState([]); | ||
|
||
const styleActive = stylesButton.active; | ||
|
||
const request = { | ||
opened: checkedOpened ? `is_open=${checkedOpened}&` : '', | ||
aroundClock: checkedAroundClock | ||
? `is_around_the_clock=${checkedAroundClock}&` | ||
: '', | ||
raiting: checkedRaiting ? `high_rating=${checkedRaiting}&` : '', | ||
services: | ||
arrFilters.length > 0 ? `services=${encodeURI(arrFilters.join())}&` : '', | ||
}; | ||
|
||
const handleChangeOpened = () => { | ||
setCheckedOpened(!checkedOpened); | ||
}; | ||
|
||
const handleChangeAroundClock = () => { | ||
setCheckedAroundClock(!checkedAroundClock); | ||
}; | ||
|
||
const handleChangeRaiting = () => { | ||
setCheckedRaiting(!checkedRaiting); | ||
}; | ||
|
||
const handleClickFilterButton = (e) => { | ||
const { classList, id, value } = e.target; | ||
if (classList.contains(styleActive)) { | ||
classList.remove(styleActive); | ||
setArrServiceButtons(arrServiceButtons.filter((item) => item.id !== id)); | ||
setArrFilters(arrFilters.filter((i) => i !== value)); | ||
} else { | ||
classList.add(styleActive); | ||
setArrServiceButtons([...arrServiceButtons, { id }]); | ||
setArrFilters([...arrFilters, value]); | ||
} | ||
}; | ||
|
||
const handleApplyfilters = () => { | ||
dispatch( | ||
fetchListFilteredCarWashes( | ||
`${request.opened}${request.aroundClock}${request.raiting}${request.services}` | ||
) | ||
); | ||
}; | ||
|
||
const handleClearFilters = () => { | ||
setCheckedOpened(false); | ||
setCheckedAroundClock(false); | ||
setCheckedRaiting(false); | ||
arrServiceButtons.map((item) => | ||
document.getElementById(item.id).classList.remove(styleActive) | ||
); | ||
dispatch( | ||
fetchListFilteredCarWashes( | ||
`${request.opened}&${request.aroundClock}&${request.raiting}&${request.services}` | ||
) | ||
); | ||
}; | ||
|
||
useEffect(() => { | ||
dispatch(fetchListServices()); | ||
dispatch(fetchListTypes()); | ||
}, [dispatch]); | ||
|
||
useEffect(() => { | ||
const closePopupHandler = (e) => { | ||
if (e.target.classList.contains(styles.opened)) { | ||
dispatch(handleOpen(false)); | ||
} | ||
}; | ||
|
||
document.addEventListener('click', closePopupHandler); | ||
return () => { | ||
document.removeEventListener('click', closePopupHandler); | ||
}; | ||
}, [dispatch]); | ||
|
||
return ( | ||
<div className={opened ? `${styles.popup} ${styles.opened}` : styles.popup}> | ||
<div className={styles.container}> | ||
<button | ||
className={styles.close} | ||
aria-label="Кнопка закрытия попапа" | ||
onClick={() => dispatch(handleOpen(false))} | ||
> | ||
<RemoveSearch /> | ||
</button> | ||
<h2 className={styles.header}>Фильтр</h2> | ||
<div className={styles.filters}> | ||
<FilterWithCheckbox | ||
onChange={handleChangeOpened} | ||
checked={checkedOpened} | ||
filterName="Открыто сейчас" | ||
/> | ||
<FilterWithCheckbox | ||
onChange={handleChangeAroundClock} | ||
checked={checkedAroundClock} | ||
filterName="Круглосуточно" | ||
/> | ||
<FilterWithServices | ||
title="Услуга" | ||
services={listServices} | ||
onClick={handleClickFilterButton} | ||
/> | ||
<FilterWithServices | ||
title="Формат" | ||
services={listTypes} | ||
onClick={handleClickFilterButton} | ||
/> | ||
<FilterWithCheckbox | ||
onChange={handleChangeRaiting} | ||
checked={checkedRaiting} | ||
filterName="Рейтинг 4+" | ||
/> | ||
</div> | ||
<div className={styles.buttons}> | ||
<button | ||
className={styles.clear} | ||
aria-label="Очистить фильтры" | ||
onClick={handleClearFilters} | ||
> | ||
<ClearFilters /> | ||
Очистить фильтры | ||
</button> | ||
<button | ||
className={styles.apply} | ||
aria-label="Применить фильтры" | ||
onClick={handleApplyfilters} | ||
> | ||
Применить фильтры | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default PopupWithFilters; |
111 changes: 111 additions & 0 deletions
111
src/components/PopupWithFilters/PopupWithFilters.module.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
.popup { | ||
position: absolute; | ||
top: 0; | ||
right: 0; | ||
bottom: 0; | ||
left: 0; | ||
z-index: 1; | ||
background-color: rgba(144, 144, 144, 0.5); | ||
opacity: 0; | ||
visibility: hidden; | ||
} | ||
|
||
.opened { | ||
opacity: 1; | ||
visibility: visible; | ||
} | ||
|
||
.container { | ||
font-family: 'Roboto', Arial, sans-serif; | ||
font-size: 20px; | ||
font-weight: 500; | ||
background-color: #fff; | ||
padding: 24px; | ||
box-sizing: border-box; | ||
max-width: 575px; | ||
width: 100%; | ||
border-radius: 15px; | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
position: fixed; | ||
top: 100px; | ||
left: 125px; | ||
} | ||
|
||
.close { | ||
border: none; | ||
background-color: inherit; | ||
align-self: flex-end; | ||
cursor: pointer; | ||
padding: 0; | ||
} | ||
|
||
.header { | ||
margin: 0; | ||
font-family: 'Manrope', Arial, sans-serif; | ||
font-size: 24px; | ||
font-weight: 700; | ||
line-height: normal; | ||
padding-top: 20px; | ||
} | ||
|
||
.filters { | ||
width: 100%; | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: center; | ||
gap: 28px; | ||
max-height: 400px; | ||
overflow-y: scroll; | ||
} | ||
|
||
.filters::-webkit-scrollbar { | ||
display: none; | ||
} | ||
|
||
.filter { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
margin: 0; | ||
width: 100%; | ||
} | ||
|
||
.buttons { | ||
display: flex; | ||
justify-content: space-between; | ||
width: 100%; | ||
align-items: center; | ||
margin-top: 32px; | ||
} | ||
|
||
.clear { | ||
border: none; | ||
cursor: pointer; | ||
background-color: inherit; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
gap: 8px; | ||
padding: 0; | ||
color: #7b7b7b; | ||
font-family: 'Roboto', Arial, sans-serif; | ||
font-size: 20px; | ||
font-weight: 500; | ||
line-height: normal; | ||
} | ||
|
||
.apply { | ||
border: none; | ||
cursor: pointer; | ||
background-color: #5568d0; | ||
padding: 0; | ||
color: white; | ||
font-family: 'Roboto', Arial, sans-serif; | ||
font-size: 20px; | ||
font-weight: 500; | ||
line-height: normal; | ||
padding: 8px 16px; | ||
border-radius: 10px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
.filter { | ||
height: 44px; | ||
width: 44px; | ||
display: flex; | ||
} | ||
|
||
|
25 changes: 25 additions & 0 deletions
25
src/components/UI/FilterWithCheckbox/FilterWithCheckbox.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import styles from './FilterWithCheckbox.module.css'; | ||
import ServiceCheckbox from '../ServiceCheckbox/ServiceCheckbox'; | ||
|
||
function FilterWithCheckbox({ filterName, onChange, checked }) { | ||
return ( | ||
<p className={styles.filter}> | ||
{filterName} | ||
<ServiceCheckbox checked={checked} onChange={onChange} /> | ||
</p> | ||
); | ||
} | ||
|
||
FilterWithCheckbox.propTypes = { | ||
filterName: PropTypes.string, | ||
onChange: PropTypes.func, | ||
checked: PropTypes.bool, | ||
}; | ||
|
||
FilterWithCheckbox.defaultProps = { | ||
filterName: '', | ||
}; | ||
|
||
export default FilterWithCheckbox; |
7 changes: 7 additions & 0 deletions
7
src/components/UI/FilterWithCheckbox/FilterWithCheckbox.module.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
.filter { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
margin: 0; | ||
width: 100%; | ||
} |
28 changes: 28 additions & 0 deletions
28
src/components/UI/FilterWithServices/FilterWithServices.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import PropTypes from 'prop-types'; | ||
import styles from './FilterWithServices.module.css'; | ||
import ServiceButton from '../ServiceButton/ServiceButton'; | ||
|
||
function FilterWithServices({ title, services, onClick }) { | ||
return ( | ||
<div className={styles.container}> | ||
<h2 className={styles.title}>{title}</h2> | ||
<div className={styles.filters}> | ||
{services.map((service) => ( | ||
<ServiceButton | ||
key={service.name} | ||
value={service.name.toLowerCase()} | ||
onClick={onClick} | ||
/> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
FilterWithServices.propTypes = { | ||
onClick: PropTypes.func, | ||
title: PropTypes.string, | ||
services: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string)), | ||
}; | ||
|
||
export default FilterWithServices; |
Oops, something went wrong.