diff --git a/src/App.jsx b/src/App.jsx index 0265ae1..6f27804 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -21,7 +21,7 @@ function App() { -
+
} /> diff --git a/src/components/CategorySelect.jsx b/src/components/CategorySelect.jsx new file mode 100644 index 0000000..886f1d3 --- /dev/null +++ b/src/components/CategorySelect.jsx @@ -0,0 +1,106 @@ +import { useEffect, useState } from 'react'; +import PropTypes from 'prop-types'; +import { categories } from '../consts'; +import { search } from '../assets'; + +const CategorySelect = props => { + const [inputValue, setInputValue] = useState(''); + const [suggestions, setSuggestions] = useState([]); + const [selectedCategoryIds, setSelectedCategoryIds] = useState([]); + + useEffect(() => { + console.log(suggestions); + console.log(selectedCategoryIds); + }, [suggestions, selectedCategoryIds]); + + useEffect(() => { + if (props.searchQuery && props.searchQuery.categories) { + setSelectedCategoryIds(props.searchQuery.categories); + } + }, [props.searchQuery]); + + // useEffect(() =>{ + // setSelectedCategoryIds([]) + // }, [props.clearSearchQuery]) + + const handleInputChange = (e) => { + const value = e.target.value; + setInputValue(value); + if (value.length > 0) { + setSuggestions(categories.filter(category => + category.name.toLowerCase().includes(value.toLowerCase()) && + !selectedCategoryIds.includes(category.id) + )); + } else { + setSuggestions([]); + } + } + + const handleSuggestionClick = (suggestion) => { + if (!selectedCategoryIds.includes(suggestion.id)) { + const newSelectedIds = [...selectedCategoryIds, suggestion.id]; + setSelectedCategoryIds(newSelectedIds); + props.setSearchQuery(prevState => ({...prevState, categories: newSelectedIds })); + } + setInputValue(''); + setSuggestions([]); + } + + const handleRemoveCategory = (id) => { + const newSelectedIds = selectedCategoryIds.filter(prevId => prevId !== id); + setSelectedCategoryIds(newSelectedIds); + props.setSearchQuery({ categories: newSelectedIds }); + } + + return ( +
+

Kategorie

+
+ {selectedCategoryIds.map(id => { + const category = categories.find(cat => cat.id === id); + return ( +
handleRemoveCategory(id)} + className='min-w-fit w-8 py-1 px-2 mr-3 border-2 hover:bg-gray-300 rounded-xl hover:cursor-pointer ' + > +

{category.name} + × +

+
+ ); + })} +
+ + {inputValue.length >= 1 && suggestions.length > 0 && ( +
+ {suggestions.map(suggestion => ( +
handleSuggestionClick(suggestion)} + > + {suggestion.name} +
+ ))} +
+ )} +
+ ) +} + +CategorySelect.propTypes = { + setSearchQuery: PropTypes.func.isRequired, + searchQuery: PropTypes.shape({ + categories: PropTypes.arrayOf(PropTypes.string) + }) + +} + +export default CategorySelect \ No newline at end of file diff --git a/src/components/Cookies.jsx b/src/components/Cookies.jsx index f8de240..ddcdc57 100644 --- a/src/components/Cookies.jsx +++ b/src/components/Cookies.jsx @@ -11,11 +11,11 @@ const Cookies = props => { if (!show) return null; return ( <> -
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

- + Wyrażenie zgody
diff --git a/src/components/Filter.jsx b/src/components/Filter.jsx index b92e93f..33fc1dd 100644 --- a/src/components/Filter.jsx +++ b/src/components/Filter.jsx @@ -1,91 +1,185 @@ - -import Search from './Search' -import { useEffect, useState } from 'react'; -import { categories, work_from_home, employment_types } from '../consts'; -import propTypes from 'prop-types'; +import { useCallback, useEffect, useMemo } from 'react'; +import PropTypes from 'prop-types'; +import Search from './Search'; import Selector from './Selector'; -const Filter = (props) => { - useEffect(() => { - console.log(props.searchQuery) - }, [props.searchQuery]); +import RangeSlider from './RangeSlider'; +import CategorySelect from './CategorySelect'; +import { categories, work_from_home, employment_types } from '../consts'; + +const Filter = ({ isOpen, searchQuery, setSearchQuery, clearSearchQuery, onClick }) => { + const [min_salary, max_salary] = [0, 100000]; + + useEffect(() => { + console.log(searchQuery); + }, [searchQuery]); - const handle_checked_change = (e) => { - const { name, value, checked } = e.target; - props.setSearchQuery(prevState => { - const newArray = checked - ? [...(prevState[name] || []), value] - : (prevState[name] || []).filter(item => item !== value); - return { ...prevState, [name]: newArray }; - }); - } - + const handleRangeChange = useCallback((newMin, newMax) => { + setSearchQuery(prevState => ({ + ...prevState, + min_salary: newMin, + max_salary: newMax + })); + }, [setSearchQuery]); + const handleChange = useCallback((e, name) => { + const value = e.target.value; + const numValue = parseInt(value, 10); + + setSearchQuery(prevState => { + switch (name) { + case 'name': + return { ...prevState, name: value }; + case 'min_salary': + if (value === '' || isNaN(numValue)) { + return { ...prevState, min_salary: '' }; + } + return { ...prevState, min_salary: numValue }; + case 'max_salary': + if (value === '' || isNaN(numValue)) { + return { ...prevState, max_salary: '' }; + } + return { ...prevState, max_salary: numValue }; + default: + return prevState; + } + }); + }, [setSearchQuery]); + + const handleCheckedChange = useCallback((e) => { + const { name, value, checked } = e.target; + setSearchQuery(prevState => { + // Sprawdź, czy dla danej nazwy już istnieje tablica w stanie + if (!prevState[name]) { + prevState[name] = []; + } + + if (checked) { + // Jeśli checkbox jest zaznaczony, dodaj wartość do odpowiedniej tablicy + return { + ...prevState, + [name]: [...prevState[name], value] + }; + } else { + // Jeśli checkbox jest odznaczony, usuń wartość z odpowiedniej tablicy + return { + ...prevState, + [name]: prevState[name].filter(item => item !== value) + }; + } + }); + }, [setSearchQuery]); + + const filterButtonClass = useMemo(() => ( + 'bg-gray-600 text-white py-1 sm:py-3 px-12 mx-auto sm:mx-40 col-span-2 rounded-md font-semibold ' + + 'text-xl hover:bg-gray-500 duration-300 hover:scale-110 my-3 sm:my-5 sm:min-h-full' + ), []); return ( - // idea - make the page whole page grayed out and only the filter is visible -
-
-
-

Filtry

- - +
+ + handleChange(e, 'name')} + /> + +
+ handleChange(e, 'min_salary')} + /> + handleChange(e, 'max_salary')} + /> + +
+ + {/* */} + {/* + */} + +
- - - - - - - - - - -
- ) + ); }; Filter.propTypes = { - isOpen: propTypes.bool, - searchQuery: propTypes.object, - setSearchQuery: propTypes.func, - categories: propTypes.array, - clearSearchQuery: propTypes.func, - onClick: propTypes.func + isOpen: PropTypes.bool.isRequired, + searchQuery: PropTypes.shape({ + name: PropTypes.string, + min_salary: PropTypes.number, + max_salary: PropTypes.number, + categories: PropTypes.arrayOf(PropTypes.string), + work_from_home: PropTypes.arrayOf(PropTypes.string), + employment_types: PropTypes.arrayOf(PropTypes.string) + }), + setSearchQuery: PropTypes.func.isRequired, + clearSearchQuery: PropTypes.func.isRequired, + onClick: PropTypes.func.isRequired }; Filter.defaultProps = { searchQuery: { categories: [], work_from_home: [], - employment: [] + employment_types: [] } }; -export default Filter; +export default Filter; \ No newline at end of file diff --git a/src/components/RangeSlider.jsx b/src/components/RangeSlider.jsx new file mode 100644 index 0000000..2201fa4 --- /dev/null +++ b/src/components/RangeSlider.jsx @@ -0,0 +1,76 @@ +import { useRef, useEffect, useCallback } from 'react'; +import propTypes from 'prop-types'; + + +const RangeSlider = ({ min, max, minVal, maxVal, setSearchQuery, onRangeChange, isOpen }) => { + const minValRef = useRef(minVal); + const maxValRef = useRef(maxVal); + const range = useRef(null); + + const getPercent = useCallback( + (value) => Math.round(((value - min) / (max - min)) * 100), + [min, max] + ); + + useEffect(() => { + const minPercent = getPercent(minVal === '' ? min : minVal); + const maxPercent = getPercent(maxVal === '' ? max : maxVal); + + if (range.current) { + range.current.style.left = `${minPercent}%`; + range.current.style.width = `${maxPercent - minPercent}%`; + } + }, [minVal, maxVal, getPercent, min, max]); + + const handleChange = useCallback((event) => { + const { id, value } = event.target; + const numValue = Number(value); + + let newMinVal = minVal === '' ? min : minVal; + let newMaxVal = maxVal === '' ? max : maxVal; + + if (id === 'range1') { + newMinVal = numValue; + minValRef.current = newMinVal; + } else if (id === 'range2') { + newMaxVal = numValue; + maxValRef.current = newMaxVal; + } + + onRangeChange(newMinVal, newMaxVal); + }, [minVal, maxVal, min, max, onRangeChange]); + + return ( +
+ (maxVal === '' ? max - 100 : maxVal - 100) ? 5 : undefined, } : { display: 'none' }} + /> + + +
+
+
+
+
+ ); +}; + +export default RangeSlider; \ No newline at end of file diff --git a/src/components/Salary.jsx b/src/components/Salary.jsx index de97710..fe016c9 100644 --- a/src/components/Salary.jsx +++ b/src/components/Salary.jsx @@ -82,7 +82,7 @@ const Salary = ({handleChange, formData, removeFields, setFormData}) => { className={`h-12 border-2 rounded-xl px-5 mx-4 w-full ${!require_salary ? 'bg-slate-200' : ''} `}/>
-

Typ kontraktu

+

Typ
kontraktu

-

Praca zdalna

+

Praca
zdalna

- {value_to_map_from.name} - - ))} -
+ {props.name} + {props.value_to_map_from.map((value_to_map_from) => ( + + ))} +
) } Selector.propTypes = { - value_to_map_from: propTypes.array, - setSearchQuery: propTypes.func, - name: propTypes.string, - inputname: propTypes.string, - onChange: propTypes.func, - state: propTypes.array + value_to_map_from: PropTypes.array, + setSearchQuery: PropTypes.func, + name: PropTypes.string, + inputname: PropTypes.string, + onChange: PropTypes.func, + state: PropTypes.object } -export default Selector +export default Selector \ No newline at end of file diff --git a/src/components/StepFourJoblisting.jsx b/src/components/StepFourJoblisting.jsx index 9dde322..3980a4d 100644 --- a/src/components/StepFourJoblisting.jsx +++ b/src/components/StepFourJoblisting.jsx @@ -102,15 +102,15 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => { -
); diff --git a/src/components/StepTwoJoblisting.jsx b/src/components/StepTwoJoblisting.jsx index c462bae..ce47a7f 100644 --- a/src/components/StepTwoJoblisting.jsx +++ b/src/components/StepTwoJoblisting.jsx @@ -1,6 +1,6 @@ import { useState } from 'react' import propTypes from 'prop-types'; - +import MaximumLength from '@ckeditor/ckeditor5-react' import { categories, experience_levels } from '../consts'; import styles from '../style' import { CKEditor } from '@ckeditor/ckeditor5-react'; @@ -17,7 +17,7 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm const data = editor.getData(); setEditorData(data); } - + const max_char = 600; const handleNextStep = () => { if (validateForm()) { console.log(errors) @@ -137,6 +137,16 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm editor={ClassicEditor} data={formData['content'] || ''} required + // config={{ + // plugins: [WordCount], + // toolbar: ['wordCount'], + // wordCount: { + // onUpdate: stats => { + // console.log(stats.words, stats.characters) + // } + // } + + // }} onChange={(event, editor) => { const data = editor.getData(); handleChange('content')(data); // Wywołanie zmodyfikowanej funkcji handleChange @@ -205,11 +215,11 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm formData={formData}/>
-
+