zmiany do mobile, filtrowanie, poprawki
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Jakub Kaniecki 2024-07-27 01:42:21 +02:00
parent 97b7e8726d
commit 45a2c8252e
14 changed files with 639 additions and 141 deletions

View File

@ -21,7 +21,7 @@ function App() {
</div>
</div>
<div className={`${styles.flexStart} mt-18 sm:mt-[6.5rem]`}>
<div className={`${styles.flexStart} mt-18 sm:mt-[4.5rem] md:mt-[6.5rem]`}>
<div className={`${styles.boxWidth2} `}>
<Routes>
<Route path="/home" element={<Home />} />

View File

@ -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 (
<div className='mb-3'>
<p className='text-center'>Kategorie</p>
<div className='flex flex-wrap gap-y-2 mt-1 '>
{selectedCategoryIds.map(id => {
const category = categories.find(cat => cat.id === id);
return (
<div
key={id}
onClick={() => 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 '
>
<p className='font-poppins text-sm'>{category.name}
<span className='relative -top-[2px] left-1'>&times;</span>
</p>
</div>
);
})}
</div>
<input
type="text"
className='border-2 rounded-lg px-3 py-1 w-full mt-2'
value={inputValue}
onChange={handleInputChange}
placeholder='Wpisz kategorię...'
/>
{inputValue.length >= 1 && suggestions.length > 0 && (
<div className='absolute h-40 w-[90%] overflow-y-auto'>
{suggestions.map(suggestion => (
<div
className='mx-3 p-3 border-2 border-gray-300 bg-gray-100 hover:bg-gray-200 cursor-pointer'
key={suggestion.id}
onClick={() => handleSuggestionClick(suggestion)}
>
{suggestion.name}
</div>
))}
</div>
)}
</div>
)
}
CategorySelect.propTypes = {
setSearchQuery: PropTypes.func.isRequired,
searchQuery: PropTypes.shape({
categories: PropTypes.arrayOf(PropTypes.string)
})
}
export default CategorySelect

View File

@ -11,11 +11,11 @@ const Cookies = props => {
if (!show) return null;
return (
<>
<div className='bg-gray-500 opacity-90 h-32 w-full fixed bottom-0 z-40 flex justify-center items-center'>
<p className=' text-black'>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
<button className='bg-white px-4 py-2 rounded-md ml-4'
<div className='bg-neutral-900 opacity-[99%] h-24 sm:h-16 w-full fixed bottom-0 z-40 flex justify-center items-center'>
<p className='mx-5 text-justify text-white font-'>Korzystamy z plików cookie, aby zapewnić prawidłowe działanie witryny. </p>
<button className= 'bg-white px-4 py-2 rounded-md ml-4 mr-4 ring-2 ring-dimWhite hover:ring-4 duration-300 font-semibold'
onClick={() => {handleCookies; setShow(!show)}}>
Zgoda</button>
Wyrażenie zgody</button>
</div>
</>

View File

@ -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
<div className={`z-10 bg-dimWhite collapsible px-3 sm:px-4 ${!props.isOpen ? '' : 'expanded border-4'}`}>
<div className='grid grid-cols-2 sm:grid-cols-3 mb-2'>
<div className='hidden sm:block'></div>
<p className='mx-auto mt-3 text-center font-poppins font-semibold text-xl text-gray-600'>Filtry</p>
<button className='rounded-xl mt-4 ph-6 w-32 bg-slate-400 hover:bg-slate-600 duration-100 ' onClick={props.clearSearchQuery}>
<span className='p-2 text-sm font-bold text-center font-poppins text-white'>Wyczyść filtry</span>
<div className={`z-10 flex flex-col bg-dimWhite collapsible px-3 sm:px-4 ${isOpen ? 'expanded border-4' : ''}`}>
<div className="grid grid-cols-2 md:grid-cols-3 mb-2">
<div className="hidden md:block" />
<p className="mx-auto mt-3 place-self-center text-center font-poppins font-semibold text-xl text-gray-600">Filtry</p>
<button
className="rounded-xl mt-4 w-32 py-1 bg-slate-400 hover:bg-slate-600 duration-100"
onClick={clearSearchQuery}
>
<span className="p-2 text-sm font-bold text-center font-poppins text-white">Wyczyść filtry</span>
</button>
</div>
<Search
label="Wyszukaj ogłoszenie"
placeholder="Wpisz nazwę stanowiska..."
name="name"
type="text"
value={searchQuery.name || ''}
onChange={(e) => handleChange(e, 'name')}
/>
<div className="grid grid-cols-2 gap-x-4">
<Search
label="Min. wynagrodzenie"
label2="wynagrodzenie"
placeholder="Wpisz kwotę minimalną..."
name="min_salary"
type="text"
value={searchQuery.min_salary || ''}
onChange={(e) => handleChange(e, 'min_salary')}
/>
<Search
label="Max. wynagrodzenie"
placeholder="Wpisz kwotę maksymalną..."
name="max_salary"
type="text"
value={searchQuery.max_salary || ''}
onChange={(e) => handleChange(e, 'max_salary')}
/>
<RangeSlider
min={min_salary}
max={max_salary}
minVal={searchQuery.min_salary || min_salary}
maxVal={searchQuery.max_salary || max_salary}
setSearchQuery={setSearchQuery}
onRangeChange={handleRangeChange}
isOpen={isOpen}
/>
</div>
<CategorySelect
setSearchQuery = {setSearchQuery}
searchQuery = {searchQuery}
clearSearchQuery = {clearSearchQuery}
/>
{/* <Selector
value_to_map_from={categories}
name="Kategorie"
inputname="categories"
onChange={handleCheckedChange}
state={searchQuery || {}}
/> */}
{/* <Selector
value_to_map_from={work_from_home}
name="Praca zdalna"
inputname="employment_types"
onChange={handleCheckedChange}
state={searchQuery || {}}
/>
<Selector
value_to_map_from={employment_types}
name="Typ kontraktu"
inputname="employment_types"
onChange={handleCheckedChange}
state={searchQuery || {}}
/> */}
<button className={filterButtonClass} onClick={onClick}>
Filtruj
</button>
</div>
<Search label='Wyszukaj ogłoszenie'
placeholder='Wpisz nazwę stanowiska...'
name='name'
setSearchQuery={props.setSearchQuery}
_type='text'
value={props.searchQuery.name || ''}/>
<Search label='Lokalizacja'
placeholder='Wpisz lokalizację...'
name='localization'
_type='text'
setSearchQuery={props.setSearchQuery}
value={props.searchQuery.localization || ''} />
<Search label='Min. wynagrodzenie'
placeholder='Wpisz kwotę minimalną...'
name='min_salary'
_type='text'
setSearchQuery={props.setSearchQuery}
value={props.searchQuery.min_salary || '' } />
<Search label='Max. wynagrodzenie'
placeholder='Wpisz kwotę maksymalną...'
name='max_salary'
setSearchQuery={props.setSearchQuery}
_type='text'
value={props.searchQuery.max_salary || ''}/>
<Selector value_to_map_from={categories} name='Kategorie' inputname='categories' onChange={handle_checked_change} state={props.searchQuery || {} }/>
<Selector value_to_map_from={work_from_home} s name='Praca zdalna' inputname='work_from_home' state={props.searchQuery || {}} onChange={handle_checked_change}/>
<Selector value_to_map_from={employment_types} name='Typ kontraktu' inputname='employment' state={props.searchQuery || {}} onChange={handle_checked_change} />
<button className='bg-gray-600 text-white py-1 sm:py-3 px-12 col-span-2 mx-auto rounded-md font-semibold text-xl hover:bg-gray-500 duration-150 my-3 sm:my-5 sm:min-h-full'
onClick={props.onClick}
>
Filtruj
</button>
</div>
)
);
};
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;

View File

@ -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 (
<div className='col-span-2 my-5 px-8 slider-wrapper'>
<input
id="range1"
type="range"
min={min}
step={100}
max={max}
value={minVal === '' ? min : minVal}
onChange={handleChange}
className={`thumb w-[85%] sm:w-[84%] thumb--left thumb--animatedin thumb--animatedout`}
style={isOpen ? { zIndex: (minVal === '' ? min : minVal) > (maxVal === '' ? max - 100 : maxVal - 100) ? 5 : undefined, } : { display: 'none' }}
/>
<input
id="range2"
type="range"
min={min}
max={max}
step={100}
value={maxVal === '' ? max : maxVal}
onChange={handleChange}
className={`thumb w-[85%] sm:w-[84%] thumb--right thumb--animatedin thumb--animatedout sm`}
style={isOpen ? {} : { display: 'none'}}
/>
<div className="slider" >
<div className="slider__track " />
<div ref={range} className="slider__range" />
</div>
</div>
);
};
export default RangeSlider;

View File

@ -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' : ''} `}/></div>
<div>
<p className='font-poppins font-semibold text-slate-700 text-center text-[14px] mt-1 mb-2'>Typ kontraktu</p>
<p className='font-poppins font-semibold text-slate-700 text-center text-[14px] mt-1 mb-2'>Typ <br className='hidden sm:block md:hidden'/>kontraktu</p>
<select
value={formData['employment_type'] || 'default' }
name='employmentType'
@ -96,7 +96,7 @@ const Salary = ({handleChange, formData, removeFields, setFormData}) => {
</select>
</div>
<div>
<p className='font-poppins font-semibold text-slate-700 text-center text-[14px] mt-1 mb-2'>Praca zdalna</p>
<p className='font-poppins font-semibold text-slate-700 text-center text-[14px] mt-1 mb-2'>Praca <br className='block md:hidden'/> zdalna</p>
<select
value={formData['work_from_home'] || 'default' }
name='workFromHome'

View File

@ -21,13 +21,13 @@ useEffect(() => {
}, [props.value])
return (
<div className='grid sm:py-1'
<div className='grid sm:py-1 '
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<label className="mx-2 text-l font- text-center font-poppins no-spin-buttons"
<label className="mx-2 text-l text-center ml-3 font-poppins no-spin-buttons"
htmlFor="search">
{props.label}
<p className='text-sm'>{props.label}</p>
<span className='text-sm text-red-700'>
{check_errors(props.value, props.name) ? '' : ' *'}
</span>
@ -38,9 +38,9 @@ useEffect(() => {
placeholder={props.placeholder}
name={props.name}
id={props.name}
onChange={(e) => props.setSearchQuery(prevState => ({...prevState, [props.name]: e.target.value}))}
className={`border-2
bg-white h-8 px-5 pr-16 mx-3 rounded-lg
onChange={props.onChange}
className={`border-2 ${ props.name === 'min_salary' || props.name === 'max_salary' ? 'w-[12rem] xs:w-[20rem] sm:w-full md:w-full lg:w-full' : '' }
bg-white h-8 px-5 rounded-lg
text-sm focus:outline-none ${check_errors(props.value, props.name) ? 'border-gray-300': 'border-red-700' }`}/>
{isHovered && <div className='text-red-700 text-sm relative'>
{check_errors(props.value, props.name) ? '' : 'Wartość musi być liczbą!'}
@ -55,7 +55,8 @@ Search.propTypes = {
name: propTypes.string,
setSearchQuery: propTypes.func,
value: propTypes.string,
_type: propTypes.string
_type: propTypes.string,
onChange: propTypes.func
}
export default Search

View File

@ -1,33 +1,35 @@
import propTypes from 'prop-types'
import PropTypes from 'prop-types'
const Selector = (props) => {
return (
<>
<div className='grid grid-cols-2 sm:gap-[1px]'>
<span className='col-span-2 mx-auto text-l font- text-center font-poppins'>{props.name}</span>
{props.value_to_map_from.map((value_to_map_from) => (
<label key={value_to_map_from.id} className
='flex items-center justify-start text-sm font-poppins font-semibold text-gray-600'>
<input type='checkbox' name={props.inputname} value={value_to_map_from.id}
onChange={props.onChange}
className='mr-2'
checked={props.state[props.inputname]?.includes(value_to_map_from.id)}
/>
{value_to_map_from.name}
</label>
))}
</div>
<span className='col-span-2 mx-auto text-l font- text-center font-poppins'>{props.name}</span>
{props.value_to_map_from.map((value_to_map_from) => (
<label key={value_to_map_from.id} className='flex items-center justify-start text-sm font-poppins font-semibold text-gray-600'>
<input
type='checkbox'
name={props.inputname}
value={value_to_map_from.id}
onChange={props.onChange}
className='mr-2'
checked={Array.isArray(props.state[props.inputname]) && props.state[props.inputname].includes(value_to_map_from.id)}
/>
{value_to_map_from.name}
</label>
))}
</div>
</>
)
}
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

View File

@ -102,15 +102,15 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
<button
type="button"
onClick={prevStep}
className="h-12 w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] scale-100 text-white hover:scale-125 duration-300"
className="h-16 md:h-12 w-80 md:w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] text-white hover:scale-125 duration-300"
>
<span className="text-[18px]"></span>&nbsp;&nbsp;Przejdź do
<span className="text-[18px]"></span>&nbsp;&nbsp;Przejdź do <br className="block md:hidden"/>
poprzedniego kroku
</button>
<button
type="button"
onClick={nextStep}
className="h-12 w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] scale-100 text-white hover:scale-125 duration-300"
className="h-16 md:h-12 w-80 md:w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] text-white hover:scale-125 duration-300"
>
Przejdź do następnego kroku &nbsp;&nbsp;
<span className="text-[18px]"></span>{" "}

View File

@ -1,6 +1,7 @@
import styles from "../style";
import propTypes from "prop-types";
import { lorem_ipsum, lorem_ipsum_premium, lorem_ipsum_starter } from "../consts";
const StepOneJoblisting = ({ nextStep, handleChange, formData }) => {
// Funkcja do obsługi kliknięcia na div i aktualizacji stanu
@ -9,48 +10,62 @@ const StepOneJoblisting = ({ nextStep, handleChange, formData }) => {
};
const activeStyle =
"h-[500px] w-64 border-4 rounded-xl border-stone-200 bg-gray-200 div-transition scale-125";
"h-[200px] sm:h-[350px] md:h-min w-64 pb-4 border-4 rounded-xl border-stone-200 bg-gray-200 div-transition scale-105 sm:scale-110";
const inactiveStyle =
"h-[500px] w-64 rounded-xl bg-gray-200 border-gray-200 div-transition cursor-pointer hover:bg-gray-300";
"h-[200px] sm:h-[350px] md:h-min w-64 pb-4 rounded-xl bg-gray-200 border-gray-200 div-transition cursor-pointer hover:bg-gray-300";
return (
<>
<div
className={` grid grid-cols-1 ${styles.paddingX} pb-8 pt-8 gap-1 h-full`}
className={`grid grid-cols-1 ${styles.paddingX} pb-8 mt-24 xs:mt-2 xs:pt-8 gap-1 h-full`}
>
<h1 className={`${styles.heading1} text-center`}>
Zacznij dodawać ogłoszenie z izaac.pl
<h1 className={`text-center text-2xl sm:text-[40px] font-bold`}>
Zacznij dodawać <br className="block md:hidden" /> ogłoszenie z izaac.pl
</h1>
<p className={`${styles.paragraph} text-center`}>
Wybierz pakiet najlepiej odpowiadający Twoim potrzebom
<p className={`text-center text-xl mt-3`}>
Wybierz pakiet najlepiej <br className="block md:hidden" />odpowiadający Twoim potrzebom
</p>
</div>
<div className={` ${styles.flexStart} ${styles.paddingX} gap-16 mt-20`}>
{/* Przykładowe divy jako przyciski wyboru */}
<div className="h-[300px] sm:h-[600px]">
<div className={` ${styles.flexStart} ${styles.paddingX} gap-3 xs:gap-5 sm:gap-8 mt-6`}>
<div
className={
formData.posting_option === "M" ? activeStyle : inactiveStyle
}
onClick={handleDivClick("M")}
>
<p className={`${styles.paragraph} text-center mt-4`}>Starter</p>
<p
className={`font-poppins text-l
sm:text-xl text-center mt-4`}>
Starter
</p>
<ul className="hidden md:block">
{lorem_ipsum_starter.map((lorem_ipsum) =>
(<li className="text-sm lista-outside mx-8 text-justify list-inside">{lorem_ipsum}</li>)
)}
</ul>
</div>
<div className="h-[400px] w-0.5 bg-gray-300"></div>
<div className="h-[150px] sm:h-[250px] md:h-[300px] w-0.5 bg-gray-300"></div>
<div
className={
formData.posting_option === "S" ? activeStyle : inactiveStyle
}
onClick={handleDivClick("S")}
>
<p className={`${styles.paragraph} text-center mt-4`}>Standard</p>
<div className="mt-6">
<p className={`${styles.paragraph} text-center`}>
<p className={`font-poppins text-l sm:text-xl text-center mt-4`}>Standard</p>
<div className="mt-2">
<p className={`font-poppins text-sm sm:text-[14px] text-center mb-2`}>
najczęsciej wybierany
</p>
<ul className="hidden md:block">
{lorem_ipsum.map((lorem_ipsum) =>
(<li className="text-sm lista-outside mx-8 text-justify list-inside">{lorem_ipsum}</li>)
)}
</ul>
</div>
</div>
<div className="h-[400px] w-0.5 bg-gray-300"></div>
<div className="h-[150px] sm:h-[250px] md:h-[300px] w-0.5 bg-gray-300"></div>
<div
className={
formData.posting_option === "P" ? activeStyle : inactiveStyle
@ -58,21 +73,26 @@ const StepOneJoblisting = ({ nextStep, handleChange, formData }) => {
onClick={handleDivClick("P")}
style={{ cursor: "pointer" }}
>
<p className={`${styles.paragraph} text-center mt-4`}>Premium</p>
<p className={`font-poppins text-l sm:text-xl text-center mt-4`}>Premium</p>
<ul className="hidden md:block">
{lorem_ipsum_premium.map((lorem_ipsum) =>
(<li className="text-sm lista-outside mx-8 text-justify list-inside">{lorem_ipsum}</li>)
)}
</ul>
</div>
</div>
<div className="grid pt-32 items-center justify-center">
</div>
<div className="w-full">
<button
type="button"
onClick={nextStep}
className="h-12 w-72 rounded-xl bg-gray-700
className="absolute inset-x-0 md:-left-4 bottom-[4rem] mx-auto h-12 w-72 rounded-xl bg-gray-700
font-poppins font-semibold text-[14px] text-white
hover:scale-125 duration-300"
>
Przejdź do następnego kroku &nbsp;&nbsp;
<span className="text-[18px]"></span>{" "}
</button>
<div className="h-12 w-full"></div>
</div>
</>
);

View File

@ -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}/>
</div>
<div className={`${styles.flexCenter} p-20 gap-20`}>
<div className={`${styles.flexCenter} py-10 gap-20`}>
<button
type="button"
onClick={prevStep}
className='h-12 w-72 rounded-xl bg-gray-700
className='h-16 md:h-12 w-80 md:w-72 rounded-xl bg-gray-700
font-poppins font-semibold text-[14px] scale-100
text-white hover:scale-125 duration-300'>
<span className='text-[18px]'></span>&nbsp;&nbsp;
@ -217,7 +227,7 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm
<button
type="button"
onClick={handleNextStep}
className='h-12 w-72 rounded-xl
className='h-16 md:h-12 w-80 md:w-72 rounded-xl
bg-gray-700 font-poppins
font-semibold text-[14px] scale-100
text-white hover:scale-125 duration-300'>

View File

@ -96,9 +96,21 @@ const WorkApp = () => {
};
const [searchQuery, setSearchQuery] = useState({});
const [searchQuery, setSearchQuery] = useState({
min_salary: '',
max_salary: '',
categories: [],
name: ''
});
const clearSearchQuery = () => {
setSearchQuery({});
setSearchQuery({
min_salary: '',
max_salary: '',
categories: [],
name: ''
});
getSkills();
setIsOpen(!isOpen)
};
const [selectedOgloszenie, setSelectedOgloszenie] = useState(ogloszenia[0]);
@ -144,13 +156,11 @@ const WorkApp = () => {
<div
className={`relative bg-gray-100 w-full sm:w-[40%]
${isOpen ? 'h-[100vh] overflow-y-hidden' : 'sm:overflow-y-auto'}
sm:h-[89vh] ${
isDetailsVisible ? "hidden sm:block" : "block"
} `}
sm:h-[89vh] ${isDetailsVisible ? "hidden sm:block" : "block" }`}
>
<div className="z-10 sticky top-[70px] sm:top-0 w-[100vw] sm:w-full">
<div className="z-10 w-full fixed sm:sticky top-[4.4rem] sm:top-0 min-h-fit bg-slate-300 flex items-center place-content-center py-1 sm:py-2">
<h1 className="text-center text-xl sm:text-2xl font-poppins font-semibold text-slate-800 py-2 mx-6">
<h1 className="text-center text-xl sm:text-l md:text-2xl font-poppins font-semibold text-slate-800 py-2 mx-2">
Oferty pracy
</h1>
<button

View File

@ -326,4 +326,35 @@ export const employment_types = [
"id": "INT",
"name": "Staż",
},
]
export const lorem_ipsum = [
"Lorem ipsum dolor sit amet",
"consectetur adipiscing elit",
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"Ut enim ad minim veniam",
"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ',
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
]
export const lorem_ipsum_starter = [
"Lorem ipsum dolor sit amet",
"consectetur adipiscing elit",
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"Ut enim ad minim veniam",
"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
]
export const lorem_ipsum_premium = [
"Lorem ipsum dolor sit amet",
"consectetur adipiscing elit",
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"Ut enim ad minim veniam",
"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
'Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. ',
"Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ",
"Ut enim ad minim veniam",
"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ",
]

View File

@ -154,6 +154,10 @@ input[type="number"] {
list-style: inside;
}
.lista-outside {
list-style: outside;
}
.sidebar-show {
-webkit-animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
animation: slide-top 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
@ -162,4 +166,148 @@ input[type="number"] {
.sidebar-hide {
-webkit-animation: slide-down 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
animation: slide-down 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
}
.slider-wrapper {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
}
.slider {
position: relative;
width: 100%;
}
.slider__track,
.slider__range,
.slider__left-value,
.slider__right-value {
position: absolute;
}
.slider__track,
.slider__range {
border-radius: 3px;
max-width: 100%;
width: 100%;
height: 5px;
}
.slider__track {
background-color: #ced4da;
width: 100;
z-index: 1;
}
.slider__range {
background-color: #9fe5e1;
z-index: 2;
}
.slider__left-value,
.slider__right-value {
color: #dee2e6;
font-size: 12px;
margin-top: 20px;
}
.slider__left-value {
left: 6px;
color: black;
}
.slider__right-value {
right: -4px;
color: black;
}
/* Removing the default appearance */
.thumb,
.thumb::-webkit-slider-thumb {
-webkit-appearance: none;
}
.thumb {
pointer-events: none;
position: absolute;
height: 0;
outline: none;
transition-duration: 0.5s;
transition-property: display;
}
.thumb--left {
z-index: 3;
}
.thumb--right {
z-index: 4;
}
/* For Chrome browsers */
.thumb::-webkit-slider-thumb {
background-color: #f1f5f7;
border: none;
border-radius: 50%;
box-shadow: 0 0 1px 1px #ced4da;
cursor: pointer;
height: 18px;
width: 18px;
margin-top: 4px;
pointer-events: all;
position: relative;
}
/* For Firefox browsers */
.thumb::-moz-range-thumb {
background-color: #f1f5f7;
border: none;
border-radius: 50%;
box-shadow: 0 0 1px 1px #ced4da;
cursor: pointer;
height: 18px;
width: 18px;
margin-top: 4px;
pointer-events: all;
position: relative;
}
@-webkit-keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.thumb--animatedin {
animation: fadeIn 0.5s;
}
@-webkit-keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.thumb--animatedout {
animation: fadeOut 0.5s;
}
.t-width {
width: 84%;
}
.search-width {
width: 16rem;
}