Merge branch 'master' into develop
This commit is contained in:
commit
c0125af058
@ -5,9 +5,9 @@ name: default
|
||||
steps:
|
||||
- name: build
|
||||
commands:
|
||||
- docker build --no-cache -t izaac-frontend-develop:latest .
|
||||
- docker tag izaac-frontend:latest registry.izaac.pl:5000/izaac-frontend-develop:latest
|
||||
- docker push registry.izaac.pl:5000/izaac-frontend-develop:latest
|
||||
- docker build --no-cache -t izaac-frontend-master:latest .
|
||||
- docker tag izaac-frontend-master:latest registry.izaac.pl:5000/izaac-frontend-master:latest
|
||||
- docker push registry.izaac.pl:5000/izaac-frontend-master:latest
|
||||
|
||||
- name: delete
|
||||
environment:
|
||||
|
||||
@ -31,7 +31,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: izaac-frontend
|
||||
image: registry.knck.pl:5000/izaac-frontend-develop:latest
|
||||
image: registry.knck.pl:5000/izaac-frontend-master:latest
|
||||
ports:
|
||||
- containerPort: 80
|
||||
volumeMounts:
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<link rel="icon" href="../icon/favicon.ico"/>
|
||||
<!-- todo ICONs -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>izaac frontend</title>
|
||||
<title>Izaac - praca dla inżynierów</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@ -1880,9 +1880,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001620",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001620.tgz",
|
||||
"integrity": "sha512-WJvYsOjd1/BYUY6SNGUosK9DUidBPDTnOARHp3fSmFO1ekdxaY6nKRttEVrfMmYi80ctS0kz1wiWmm14fVc3ew==",
|
||||
"version": "1.0.30001640",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz",
|
||||
"integrity": "sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"lint": "eslint -c eslint.config.js --ext .js,.jsx,.ts,.tsx .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
29
src/App.jsx
29
src/App.jsx
@ -7,40 +7,33 @@ import { Route, Routes } from "react-router-dom";
|
||||
import Contact from "./components/Contact";
|
||||
import Mininav from "./components/Mininav";
|
||||
import AddJobListing from "./components/AddJobListing";
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import EmployerPanel from "./components/EmployerPanel";
|
||||
import Cookies from "./components/Cookies";
|
||||
|
||||
function App() {
|
||||
|
||||
const loc = () => {
|
||||
if (location.pathname.startsWith("work/jobposting")) {
|
||||
return "";
|
||||
} else {
|
||||
return "overflow-hidden";
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Router>
|
||||
<div className={`bg-white w-full ${loc}`}>
|
||||
<div className={`${styles.paddingX} ${styles.flexCenter} border-b-2`}>
|
||||
<Mininav />
|
||||
<div className={`${styles.boxWidth}`}>
|
||||
<div className={`bg-white w-full`}>
|
||||
<div className={`${styles.paddingX} ${styles.flexCenter}`}>
|
||||
{/* <Mininav /> */}
|
||||
<div className={`${styles.boxWidth} `}>
|
||||
<NavBar />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.flexStart}`}>
|
||||
<div className={`${styles.flexStart} mt-18 sm:mt-[4.5rem] md:mt-[6.5rem]`}>
|
||||
<div className={`${styles.boxWidth2} `}>
|
||||
<Routes>
|
||||
<Route path="/home" element={<Home />} />
|
||||
<Route path="/work" element={<WorkApp />} />
|
||||
<Route path="/work/postings" element={<WorkApp />} />
|
||||
<Route path="/work/joboffers" element={<WorkApp />} />
|
||||
<Route path="/contact" element={<Contact />} />
|
||||
<Route path="/work/jobposting" element={<AddJobListing />} />
|
||||
<Route path="/employerpanel" element={<EmployerPanel />} />
|
||||
<Route path="/work/addjoboffer" element={<AddJobListing />} />
|
||||
<Route path="/work/joboffers/:id" element={<WorkApp/>} />
|
||||
{/* <Route path="/employerpanel" element={<EmployerPanel />} /> */}
|
||||
{/* Add more routes as needed */}
|
||||
</Routes>
|
||||
<Cookies />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import StepOneJoblisting from './StepOneJoblisting';
|
||||
import StepTwoJoblisting from './StepTwoJoblisting';
|
||||
import StepThreeJoblisting from './StepThreeJoblisting';
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import propTypes from 'prop-types'
|
||||
|
||||
const Category = (props) => {
|
||||
return (
|
||||
@ -15,4 +15,11 @@ const Category = (props) => {
|
||||
)
|
||||
}
|
||||
|
||||
Category.propTypes = {
|
||||
name: propTypes.string,
|
||||
func: propTypes.func,
|
||||
small: propTypes.bool,
|
||||
last: propTypes.bool
|
||||
}
|
||||
|
||||
export default Category
|
||||
|
||||
106
src/components/CategorySelect.jsx
Normal file
106
src/components/CategorySelect.jsx
Normal 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'>×</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
|
||||
29
src/components/Cookies.jsx
Normal file
29
src/components/Cookies.jsx
Normal file
@ -0,0 +1,29 @@
|
||||
import propTypes from 'prop-types'
|
||||
import { useState } from 'react'
|
||||
const Cookies = props => {
|
||||
const handleCookies = () => {
|
||||
localStorage.setItem('cookies', true)
|
||||
}
|
||||
const cookies = localStorage.getItem('cookies')
|
||||
// if(cookies) return null
|
||||
const [show, setShow] = useState(true)
|
||||
if (cookies) return null;
|
||||
if (!show) return null;
|
||||
return (
|
||||
<>
|
||||
<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)}}>
|
||||
Wyrażenie zgody</button>
|
||||
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Cookies.propTypes = {
|
||||
|
||||
}
|
||||
|
||||
export default Cookies
|
||||
@ -1,4 +1,4 @@
|
||||
import React from "react";
|
||||
|
||||
import { ogloszenia } from "../consts";
|
||||
import ListingSmall from "./ListingSmall";
|
||||
import { useState } from "react";
|
||||
|
||||
@ -1,30 +1,185 @@
|
||||
import React from 'react'
|
||||
import Search from './Search'
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Search from './Search';
|
||||
import Selector from './Selector';
|
||||
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 Filter = (props) => {
|
||||
if (!props.isOpen) return null;
|
||||
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 (
|
||||
<div className={`sticky top-24 z-20 bg-gray-100 flex flex-wrap place-items-center items-center slide-container py-2 ${!props.isOpen ? '' : 'expanded'}`}>
|
||||
<Search label='Wyszukaj ogłoszenie'
|
||||
placeholder='Wpisz nazwę stanowiska...' />
|
||||
<Search label='Lokalizacja'
|
||||
placeholder='Wpisz lokalizację...' />
|
||||
<Search label='Min. wynagrodzenie'
|
||||
placeholder='Wpisz kwotę minimalną...' />
|
||||
<Search label='Max. wynagrodzenie'
|
||||
placeholder='Wpisz kwotę maksymalną...' />
|
||||
<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')}
|
||||
/>
|
||||
|
||||
<button className='bg-gray-600 text-white py-3 px-12 col-span-2
|
||||
mx-auto rounded-md font-semibold text-xl hover:bg-gray-500 duration-150
|
||||
mt-3
|
||||
'
|
||||
onClick={props.onClicked}
|
||||
>
|
||||
Filtruj
|
||||
</button>
|
||||
<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>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default Filter
|
||||
Filter.propTypes = {
|
||||
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_types: []
|
||||
}
|
||||
};
|
||||
|
||||
export default Filter;
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
|
||||
import styles from '../style'
|
||||
const Home = () => {
|
||||
return (
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { placeholderImage } from '../assets';
|
||||
import axios from 'axios';
|
||||
|
||||
import propTypes from 'prop-types';
|
||||
|
||||
const ImageUpload = ({setFormData, data}) => {
|
||||
const [imageSrc, setImageSrc] = useState(placeholderImage);
|
||||
@ -83,4 +83,9 @@ const ImageUpload = ({setFormData, data}) => {
|
||||
);
|
||||
};
|
||||
|
||||
ImageUpload.propTypes = {
|
||||
setFormData: propTypes.func,
|
||||
data: propTypes.object
|
||||
};
|
||||
|
||||
export default ImageUpload;
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import axios from 'axios';
|
||||
import DOMPurify from 'dompurify';
|
||||
import SkillRender from './SkillRender'; // Ensure this import is correctly pointing to your SkillRender component
|
||||
import SkillRender from './SkillRender';
|
||||
|
||||
// eslint-disable-next-line react/prop-types
|
||||
const JobOfferContent = ({ id, skills }) => {
|
||||
const [jobOffer, setJobOffer] = useState(null);
|
||||
|
||||
const fetchJobOfferById = async (jobId) => {
|
||||
try {
|
||||
const response = await axios.get(`https://izaac.knck.pl/api/jobposting/joboffers/${jobId}`);
|
||||
const response = await axios.get(`https://izaac.knck.pl/api/jobposting/joboffers/${jobId}/?format=json`);
|
||||
setJobOffer(response.data);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch job offer:', error);
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import React from 'react'
|
||||
import { IzaacLOGO } from '../assets'
|
||||
|
||||
const ListingSmall = ({ id, name, company_name, min_salary, max_salary, require_salary, index, onClick, _class, image }) => {
|
||||
import { IzaacLOGO } from '../assets'
|
||||
import propTypes from 'prop-types'
|
||||
|
||||
const ListingSmall = ({ id, name, company_name, min_salary, max_salary, require_salary, index, onClick, _class, image, selected, blur }) => {
|
||||
return (
|
||||
<div
|
||||
key={id}
|
||||
className={`flex flex-row items-center gap-4 drop-shadow cursor-pointer mb-2 sm:mr-4 mr-0 text-xl font-bold border rounded-[10px] p-2 sm: px-4 hover:border-l-8 hover:border-zinc-500 duration-300 ${index % 2 === 0 ? 'bg-gray-200' : 'bg-gray-300'} ${_class}`}
|
||||
className={`flex flex-row ml-1 w-[98vw] sm:w-full items-center gap-4 drop-shadow cursor-pointer mb-2 sm:mr-4 mr-4 text-xl font-bold border rounded-[10px] p-2 sm:px-4 hover:border-l-8 hover:border-zinc-500 duration-300 blur-transition ${index % 2 === 0 ? 'bg-gray-200' : 'bg-gray-300'} ${_class} ${selected ? 'border-l-8 border-zinc-500 bg-gray-400' : 'border-l-8 border-transparent'} `}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className='flex-shrink-0 w-[6rem] h-[6rem] bg-white rounded-lg flex items-center justify-center'>
|
||||
@ -14,7 +15,7 @@ const ListingSmall = ({ id, name, company_name, min_salary, max_salary, require_
|
||||
</div>
|
||||
<div className='flex-grow flex flex-col justify-between'>
|
||||
<p className='text-base font-bold text-left tracking-wide'>{name}</p>
|
||||
<p className='text-sm text-left text-gray-600'>{company_name}</p>
|
||||
<p className='text-xs text-left text-gray-600'>{company_name}</p>
|
||||
{/* Wyświetlenie wynagrodzenia pod nazwą, jeśli jest wymagane */}
|
||||
{require_salary && (
|
||||
<p className='text-xs font-semibold text-left tracking-widest text-slate-800 mt-2'>{min_salary} - {max_salary} PLN</p>
|
||||
@ -24,4 +25,19 @@ const ListingSmall = ({ id, name, company_name, min_salary, max_salary, require_
|
||||
);
|
||||
};
|
||||
|
||||
export default ListingSmall;
|
||||
export default ListingSmall;
|
||||
|
||||
ListingSmall.propTypes = {
|
||||
id: propTypes.number,
|
||||
name: propTypes.string,
|
||||
company_name: propTypes.string,
|
||||
min_salary: propTypes.number,
|
||||
max_salary: propTypes.number,
|
||||
require_salary: propTypes.bool,
|
||||
index: propTypes.number,
|
||||
onClick: propTypes.func,
|
||||
_class: propTypes.string,
|
||||
image: propTypes.string,
|
||||
selected: propTypes.bool,
|
||||
blur: propTypes.string
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
import propTypes from 'prop-types';
|
||||
import styles from '../style';
|
||||
|
||||
const Login = ({isOpen, onClose}) => {
|
||||
@ -31,4 +31,9 @@ const Login = ({isOpen, onClose}) => {
|
||||
)
|
||||
}
|
||||
|
||||
Login.propTypes = {
|
||||
isOpen: propTypes.bool,
|
||||
onClose: propTypes.func
|
||||
}
|
||||
|
||||
export default Login
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import React, { useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import Register from './Register';
|
||||
import Login from './Login';
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
import { Route, Routes } from "react-router-dom";
|
||||
|
||||
|
||||
const Mininav = () => {
|
||||
const [isPopupOpen, setPopupOpen] = useState(false)
|
||||
@ -17,7 +16,7 @@ const Mininav = () => {
|
||||
<>
|
||||
<div className='bg-gray-500 h-5 absolute w-full top-0 flex justify-end items-center z-50'>
|
||||
{/* Link otwierający modal */}
|
||||
<a href="/employerpanel" className='text-white font-poppins font-semibold text-xs'>Panel pracodawcy</a>
|
||||
{/* <a href="/employerpanel" className='text-white font-poppins font-semibold text-xs'>Panel pracodawcy</a> */}
|
||||
<button onClick={openPopup} className='text-white font-poppins font-semibold text-xs mx-12'>
|
||||
Rejestracja
|
||||
</button>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import React from 'react'
|
||||
import { linki, linki_home } from '../consts';
|
||||
import { close, search, menu, IzaacLOGO} from '../assets';
|
||||
import { useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Mininav from './Mininav';
|
||||
|
||||
const NavBar = () => {
|
||||
const[toggle, setToggle] = useState(false)
|
||||
const[toggleSearch, setToggleSearch] = useState(false)
|
||||
@ -20,10 +20,10 @@ const NavBar = () => {
|
||||
const currentLinks = getLinks()
|
||||
|
||||
return (
|
||||
<nav className='w-full flex pt-7 justify-between items-center navbar '>
|
||||
|
||||
<nav className='fixed top-0 left-0 z-30 w-full flex pt-4 justify-between items-center navbar bg-white px-8 border-b-2 xl:px-64'>
|
||||
<a href='/' className='font-popins font-semibold text-[24px] text-slate-900'>
|
||||
<img src={IzaacLOGO} alt="izaac" className='md:w-[200px] w-[120px]'/>
|
||||
|
||||
</a>
|
||||
<ul className='list-none sm:flex hidden justify-end items-center flex-1'>
|
||||
{currentLinks.map((nav, index) => (
|
||||
<li
|
||||
@ -76,5 +76,4 @@ const NavBar = () => {
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default NavBar
|
||||
|
||||
76
src/components/RangeSlider.jsx
Normal file
76
src/components/RangeSlider.jsx
Normal 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;
|
||||
@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import styles from '../style';
|
||||
import propTypes from 'prop-types';
|
||||
|
||||
const Register = ({isOpen, onClose}) => {
|
||||
if (!isOpen) return null;
|
||||
@ -44,5 +44,8 @@ const Register = ({isOpen, onClose}) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Register.propTypes = {
|
||||
isOpen: propTypes.bool,
|
||||
onClose: propTypes.func
|
||||
}
|
||||
export default Register
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import styles from '../style'
|
||||
import { useState, useEffect } from 'react'
|
||||
import propTypes from 'prop-types'
|
||||
|
||||
import {employment_types, work_from_home } from '../consts'
|
||||
|
||||
@ -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'
|
||||
@ -112,5 +112,18 @@ const Salary = ({handleChange, formData, removeFields, setFormData}) => {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Salary.propTypes = {
|
||||
handleChange: propTypes.func,
|
||||
formData: propTypes.object,
|
||||
removeFields: propTypes.func,
|
||||
setFormData: propTypes.func,
|
||||
min_salary: propTypes.string,
|
||||
max_salary: propTypes.string,
|
||||
require_salary: propTypes.bool,
|
||||
employment_type: propTypes.string,
|
||||
work_from_home: propTypes.string
|
||||
|
||||
}
|
||||
|
||||
export default Salary
|
||||
|
||||
|
||||
@ -1,19 +1,62 @@
|
||||
import React from 'react'
|
||||
|
||||
import { useEffect, useState } from 'react'
|
||||
import propTypes from 'prop-types'
|
||||
|
||||
const Search = (props) => {
|
||||
return (
|
||||
<div className='grid py-2'>
|
||||
<label className="mx-2 text-l font- text-center font-poppins"
|
||||
const [isHovered, setIsHovered] = useState(false)
|
||||
const check_errors = (value, name) => {
|
||||
if ((name === 'max_salary' || name === 'min_salary') && !(value === '' || value === undefined)) {
|
||||
if (isNaN(parseInt(value))) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
console.log(props.value)
|
||||
}, [props.value])
|
||||
|
||||
return (
|
||||
<div className='grid sm:py-1 '
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<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>
|
||||
</label>
|
||||
<input type="text"
|
||||
placeholder={props.placeholder}
|
||||
className="border-2 border-gray-300
|
||||
bg-white h-8 px-5 pr-16 mx-3 rounded-lg
|
||||
text-sm focus:outline-none" />
|
||||
<input
|
||||
type={props._type}
|
||||
value={props.value || ''}
|
||||
placeholder={props.placeholder}
|
||||
name={props.name}
|
||||
id={props.name}
|
||||
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ą!'}
|
||||
</div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Search.propTypes = {
|
||||
label: propTypes.string,
|
||||
placeholder: propTypes.string,
|
||||
name: propTypes.string,
|
||||
setSearchQuery: propTypes.func,
|
||||
value: propTypes.string,
|
||||
_type: propTypes.string,
|
||||
onChange: propTypes.func
|
||||
}
|
||||
|
||||
export default Search
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import propTypes from 'prop-types';
|
||||
|
||||
const levelMappings = {
|
||||
'N': 'Nice to have',
|
||||
@ -53,4 +53,12 @@ const SelectedSkill = ({ skill_name, skillId ,letter, onLevelChange, removeSkill
|
||||
);
|
||||
};
|
||||
|
||||
SelectedSkill.propTypes = {
|
||||
skill_name: propTypes.string,
|
||||
skillId: propTypes.number,
|
||||
letter: propTypes.string,
|
||||
onLevelChange: propTypes.func,
|
||||
removeSkillFromList: propTypes.func,
|
||||
};
|
||||
|
||||
export default SelectedSkill;
|
||||
|
||||
35
src/components/Selector.jsx
Normal file
35
src/components/Selector.jsx
Normal file
@ -0,0 +1,35 @@
|
||||
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={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.object
|
||||
}
|
||||
|
||||
export default Selector
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import propTypes from 'prop-types';
|
||||
import SkillRender from './SkillRender';
|
||||
|
||||
|
||||
@ -15,5 +15,9 @@ const SkillsList = ({skillData, skill_names}) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
SkillsList.propTypes = {
|
||||
skillData: propTypes.array,
|
||||
skill_names: propTypes.array,
|
||||
};
|
||||
|
||||
export default SkillsList;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import React from 'react'
|
||||
|
||||
import propTypes from 'prop-types';
|
||||
|
||||
const levelMappings = {
|
||||
'N': 'Nice to have',
|
||||
@ -31,7 +30,7 @@ const SkillRender = ({key, skill, level,}) => {
|
||||
|
||||
return (
|
||||
|
||||
<div className='w-64 h-42 rounded-[15px] bg-slate-200 py-4 m-2 hover:bg-slate-300 drop-shadow-sm' key={key}>
|
||||
<div className='h-42 rounded-[15px] bg-slate-200 py-4 m-2 hover:bg-slate-300 drop-shadow-sm' key={key}>
|
||||
<p className='text-center font-bold mb-1 px-2'>{skill}</p>
|
||||
<div className='h-0.5 mx-auto w-52 mt-2 justify-center bg-dimWhite opacity-60'></div>
|
||||
|
||||
@ -42,4 +41,10 @@ const SkillRender = ({key, skill, level,}) => {
|
||||
)
|
||||
};
|
||||
|
||||
SkillRender.propTypes = {
|
||||
key: propTypes.number,
|
||||
skill: propTypes.string,
|
||||
level: propTypes.string,
|
||||
};
|
||||
|
||||
export default SkillRender;
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useState, useEffect } from 'react';
|
||||
import propTypes from 'prop-types';
|
||||
import SelectedSkill from './SelectedSkill';
|
||||
import axios from 'axios';
|
||||
|
||||
const SkillsSelector = ({ formData, setFormData, skills }) => {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
@ -101,4 +101,10 @@ const SkillsSelector = ({ formData, setFormData, skills }) => {
|
||||
);
|
||||
};
|
||||
|
||||
SkillsSelector.propTypes = {
|
||||
formData: propTypes.object.isRequired,
|
||||
setFormData: propTypes.func.isRequired,
|
||||
skills: propTypes.array.isRequired
|
||||
};
|
||||
|
||||
export default SkillsSelector;
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
import React from "react";
|
||||
import propTypes from "prop-types";
|
||||
|
||||
import styles from "../style";
|
||||
import TextDivider from "./TextDivider";
|
||||
|
||||
|
||||
const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
|
||||
return (
|
||||
<div className={`${styles.flexCenter}`}>
|
||||
<div className="grid grid-cols-5">
|
||||
<div className="col-span-3 col-start-2 mt-12">
|
||||
<TextDivider text={`Podaj dane ogłoszeniodawcy`} />
|
||||
<div className="grid grid-cols-3">
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
Imię
|
||||
@ -21,8 +24,9 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
|
||||
id="first_name"
|
||||
onChange={handleChange("first_name")}
|
||||
placeholder="Twoje imię..."
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph}`}
|
||||
className={`px-4 mt-2 ${styles.paragraph}`}
|
||||
/>
|
||||
<div className="bg-black h-0.5 opacity-10"></div>
|
||||
</div>
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
@ -37,8 +41,9 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
|
||||
id="last_name"
|
||||
onChange={handleChange("last_name")}
|
||||
placeholder="Twoje nazwisko..."
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph}`}
|
||||
className={`px-4 mt-2 ${styles.paragraph}`}
|
||||
/>
|
||||
<div className="bg-black h-0.5 opacity-10"></div>
|
||||
</div>
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
@ -53,15 +58,15 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
|
||||
id="contact_email"
|
||||
onChange={handleChange("contact_email")}
|
||||
placeholder="Adres mailowy..."
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph}`}
|
||||
className={`px-4 mt-2 ${styles.paragraph}`}
|
||||
/>
|
||||
<div className="bg-black h-0.5 opacity-10"></div>
|
||||
</div>
|
||||
<div className="col-span-2 mt-4 mx-2">
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
Nazwa firmy
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2">
|
||||
<input
|
||||
type="text"
|
||||
name="company_name"
|
||||
@ -70,9 +75,9 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
|
||||
onChange={handleChange("company_name")}
|
||||
id="company_name"
|
||||
placeholder="Wpisz nazwę firmy..."
|
||||
className={`border-b-2 px-4 mr-12 my-2 ${styles.paragraph} col-span-2 `}
|
||||
className={`px-4 mt-2 ${styles.paragraph}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="bg-black h-0.5 opacity-10"></div>
|
||||
</div>
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
@ -88,23 +93,24 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
|
||||
onChange={handleChange("vat_number")}
|
||||
id="vat_number"
|
||||
placeholder="Wpisz nip..."
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph} `}
|
||||
/>
|
||||
className={`px-4 mt-2 ${styles.paragraph}`}
|
||||
/>
|
||||
<div className="bg-black h-0.5 opacity-10"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${styles.flexCenter} gap-16 mt-16`}>
|
||||
<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> Przejdź do
|
||||
<span className="text-[18px]">←</span> 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
|
||||
<span className="text-[18px]">→</span>{" "}
|
||||
@ -117,4 +123,11 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
|
||||
);
|
||||
};
|
||||
|
||||
StepFourJoblisting.propTypes = {
|
||||
handleChange: propTypes.func,
|
||||
formData: propTypes.object,
|
||||
nextStep: propTypes.func,
|
||||
prevStep: propTypes.func,
|
||||
};
|
||||
|
||||
export default StepFourJoblisting;
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
import React from "react";
|
||||
|
||||
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
|
||||
const handleDivClick = (value) => () => {
|
||||
@ -7,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
|
||||
@ -56,24 +73,35 @@ 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
|
||||
<span className="text-[18px]">→</span>{" "}
|
||||
</button>
|
||||
<div className="h-12 w-full"></div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
StepOneJoblisting.propTypes = {
|
||||
nextStep: propTypes.func,
|
||||
handleChange: propTypes.func,
|
||||
formData: propTypes.object,
|
||||
};
|
||||
|
||||
export default StepOneJoblisting;
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import DOMPurify from 'dompurify';
|
||||
import styles from '../style';
|
||||
import TextDivider from './TextDivider';
|
||||
import ListingSmall from './ListingSmall';
|
||||
import { IzaacLOGO } from '../assets'
|
||||
|
||||
import SkillsList from './SkillList';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const StepThreeJoblisting = ({ formData, prevStep, skills, handleSubmit }) => {
|
||||
// Funkcja do przetwarzania HTML i dodawania klas
|
||||
const processHTML = (htmlString) => {
|
||||
@ -103,4 +104,11 @@ const StepThreeJoblisting = ({ formData, prevStep, skills, handleSubmit }) => {
|
||||
);
|
||||
}
|
||||
|
||||
StepThreeJoblisting.propTypes = {
|
||||
formData: PropTypes.object,
|
||||
prevStep: PropTypes.func,
|
||||
skills: PropTypes.array,
|
||||
handleSubmit: PropTypes.func
|
||||
}
|
||||
|
||||
export default StepThreeJoblisting;
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
import React, { useState } from 'react'
|
||||
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';
|
||||
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
@ -7,8 +10,6 @@ import SkillsSelector from './SkillsSelector';
|
||||
import Salary from './Salary';
|
||||
import { placeholderImage } from '../assets';
|
||||
|
||||
import { categories, experience_levels } from '../consts';
|
||||
|
||||
const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setFormData, removeFields, skills }) => {
|
||||
const [editorData, setEditorData] = useState('');
|
||||
const [imageSrc, setImageSrc] = useState(placeholderImage);
|
||||
@ -16,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)
|
||||
@ -136,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 +216,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>
|
||||
@ -217,7 +228,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'>
|
||||
@ -231,4 +242,14 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm
|
||||
)
|
||||
}
|
||||
|
||||
StepTwoJoblisting.propTypes = {
|
||||
nextStep: propTypes.func,
|
||||
prevStep: propTypes.func,
|
||||
handleChange: propTypes.func,
|
||||
formData: propTypes.object,
|
||||
setFormData: propTypes.func,
|
||||
removeFields: propTypes.func,
|
||||
skills: propTypes.array
|
||||
}
|
||||
|
||||
export default StepTwoJoblisting
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React from 'react'
|
||||
|
||||
import TextDivider from './TextDivider'
|
||||
|
||||
const Success = () => {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import React from 'react'
|
||||
|
||||
const TextDivider = ({text }) => {
|
||||
import propTypes from 'prop-types'
|
||||
|
||||
const TextDivider = ({ text }) => {
|
||||
return (
|
||||
<div className='flex items-center justify-center mt-4 mb-6'>
|
||||
<div className='h-0.5 bg-black w-[14rem] opacity-30'></div>
|
||||
<p className='text-[24px] mx-8 font-semibold'>
|
||||
<p className='text-[24px] mx-8 font-semibold text-center'>
|
||||
{text}
|
||||
</p>
|
||||
<div className='h-0.5 bg-black w-[14rem] opacity-30'></div>
|
||||
@ -13,4 +14,7 @@ const TextDivider = ({text }) => {
|
||||
)
|
||||
}
|
||||
|
||||
TextDivider.propTypes = {
|
||||
text: propTypes.string
|
||||
}
|
||||
export default TextDivider
|
||||
|
||||
@ -2,13 +2,15 @@ import React from "react";
|
||||
|
||||
import { useState, useEffect } from "react";
|
||||
import ListingSmall from "./ListingSmall";
|
||||
import SkillRender from "./SkillRender";
|
||||
import Search from "./Search";
|
||||
// import SkillRender from "./SkillRender";
|
||||
// import Search from "./Search";
|
||||
import Filter from "./Filter";
|
||||
import Category from "./Category";
|
||||
// import Category from "./Category";
|
||||
import JobOfferContent from "./JobOfferContent";
|
||||
import { categories } from "../consts";
|
||||
import axios from "axios";
|
||||
import { Link, useParams } from "react-router-dom";
|
||||
|
||||
|
||||
function renderCircles(level) {
|
||||
let numberOfFilledCircles;
|
||||
@ -44,11 +46,17 @@ function renderCircles(level) {
|
||||
}
|
||||
|
||||
const WorkApp = () => {
|
||||
let { id } = useParams();
|
||||
|
||||
const [isDetailsVisible, setIsDetailsVisible] = useState(false);
|
||||
// Czy szczegóły są widoczne
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
// Funkcja pobierająca dane z API
|
||||
// useEffect(() => {
|
||||
// if (!isOpen) {
|
||||
// setSearchQuery({ name: "", localization: "", min_salary: "", max_salary: "" });
|
||||
// }}, [isOpen]);
|
||||
|
||||
const [ogloszenia, setOgloszenia] = useState([]);
|
||||
|
||||
const [skills, setSkills] = useState([]);
|
||||
@ -64,12 +72,15 @@ const WorkApp = () => {
|
||||
// console.log(data);
|
||||
if (data.length > 0) {
|
||||
setSelectedOgloszenie(data[0]);
|
||||
// console.log(data[0])
|
||||
console.log(data[0])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
if (id === undefined) {
|
||||
id = ogloszenia[0]?.id;
|
||||
}
|
||||
|
||||
const getSkills = async () => {
|
||||
try {
|
||||
@ -85,27 +96,40 @@ const WorkApp = () => {
|
||||
};
|
||||
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState({});
|
||||
const [searchQuery, setSearchQuery] = useState({
|
||||
min_salary: '',
|
||||
max_salary: '',
|
||||
categories: [],
|
||||
name: ''
|
||||
});
|
||||
const clearSearchQuery = () => {
|
||||
setSearchQuery({
|
||||
min_salary: '',
|
||||
max_salary: '',
|
||||
categories: [],
|
||||
name: ''
|
||||
});
|
||||
getSkills();
|
||||
setIsOpen(!isOpen)
|
||||
};
|
||||
|
||||
const [selectedOgloszenie, setSelectedOgloszenie] = useState(ogloszenia[0]);
|
||||
|
||||
useEffect(() => {
|
||||
getOgloszenia();
|
||||
getSkills();
|
||||
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.title = `Izaac - ${selectedOgloszenie?.name}`;
|
||||
}, [selectedOgloszenie?.name]);
|
||||
|
||||
const handleOgloszenieClick = (ogloszenie) => {
|
||||
setSelectedOgloszenie(ogloszenie);
|
||||
setIsDetailsVisible(true); // Pokaż szczegóły na urządzeniach mobilnych
|
||||
};
|
||||
const prevSlide = () => {
|
||||
setCurrentSlide(
|
||||
(prevSlide) => (prevSlide - 1 + categories.length) % categories.length
|
||||
);
|
||||
};
|
||||
const nextSlide = () => {
|
||||
setCurrentSlide((prevSlide) => (prevSlide + 1) % categories.length);
|
||||
};
|
||||
const [currentSlide, setCurrentSlide] = React.useState(0);
|
||||
|
||||
// Funkcja do powrotu do listy ogłoszeń
|
||||
const handleBackToList = () => {
|
||||
setIsDetailsVisible(false);
|
||||
@ -125,53 +149,48 @@ const WorkApp = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section className=" mx-auto bg-white w-full">
|
||||
<div className="flex-grow h-5/6 grid grid-cols-10">
|
||||
<div className="px-3 flex col-span-11 sm:flex-wrap flex-shrink-0 items-center place-content-center">
|
||||
<button onClick={prevSlide} className="lg:hidden mx-6 font-semibold place-self-start self-center leading-5">
|
||||
Poprzednia <br/> kategoria
|
||||
</button>
|
||||
{categories.map((category, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`lg:block ${
|
||||
index === currentSlide ? "block" : "hidden"
|
||||
} lg:flex flex-grow lg:flex-none flex-basis-0"`}
|
||||
>
|
||||
<Category
|
||||
name={category.name}
|
||||
last={index === categories.length - 1}
|
||||
small={isSmallViewport}
|
||||
func={() => { getOgloszenia({ category: category.id }); }}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<button onClick={nextSlide} className="lg:hidden font-semibold leading-5 place-self-end self-center mr-6">
|
||||
Następna <br/> kategoria
|
||||
</button>
|
||||
</div>
|
||||
<section className={`bg-white`}>
|
||||
<div className="h-[88vh] flex flex-row ">
|
||||
|
||||
|
||||
<div
|
||||
className={`static rounded-xl col-span-11 sm:col-span-4 bg-gray-100 overflow-y-auto px-2 my-4 mx-2 sm:h-[84vh] h-[84vh] ${
|
||||
isDetailsVisible ? "hidden sm:block" : "block"
|
||||
}`}
|
||||
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" }`}
|
||||
>
|
||||
<div className="z-10 sticky top-0 col-span-11 sm:cols-span-4 h-24 bg-slate-300 flex items-center place-content-center mb-2">
|
||||
<h1 className="text-center text-3xl font-poppins font-semibold text-slate-800 py-4 mx-6">
|
||||
<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-l md:text-2xl font-poppins font-semibold text-slate-800 py-2 mx-2">
|
||||
Oferty pracy
|
||||
</h1>
|
||||
<button
|
||||
className="block font-poppins text-white font-semibold
|
||||
bg-gray-500 px-4 py-2 mx-6 rounded-md"
|
||||
bg-gray-500 px-4 py-2 mx-6 rounded-md hover:bg-gray-600 duration-100"
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>
|
||||
Pokaż filtry
|
||||
|
||||
{!isOpen ? 'Pokaż filtry' : 'Ukryj filtry'}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<div className="z-10 sticky top-24 col-span-11 sm:cols-span-4 bg-slate-300 mb-2">
|
||||
{isOpen && <Filter isOpen={isOpen} />}
|
||||
<div className={`z-10 absolute top-[3rem] sm:top-16 w-[100vw] sm:w-full`}>
|
||||
<Filter isOpen={isOpen}
|
||||
searchQuery={searchQuery || {}}
|
||||
setSearchQuery={setSearchQuery}
|
||||
categories={categories}
|
||||
clearSearchQuery={clearSearchQuery}
|
||||
onClick={()=> {getOgloszenia(searchQuery)
|
||||
setIsOpen(!isOpen)
|
||||
}}/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`sm:mt-2 mr-2 mt-[8rem] noblur ${!isOpen ? '' : 'blur'}`}
|
||||
|
||||
>
|
||||
{ogloszenia.map((ogloszenie, index) => (
|
||||
<Link
|
||||
key={ogloszenie.id}
|
||||
to={`/work/joboffers/${ogloszenie.id}`} >
|
||||
<ListingSmall
|
||||
key={ogloszenie.id}
|
||||
name={ogloszenie.name}
|
||||
@ -181,14 +200,18 @@ const WorkApp = () => {
|
||||
require_salary={ogloszenie.require_salary}
|
||||
image={ogloszenie.image}
|
||||
index={index}
|
||||
onClick={() => handleOgloszenieClick(ogloszenie)}
|
||||
onClick={() => {handleOgloszenieClick(ogloszenie)}}
|
||||
selected={selectedOgloszenie?.id === ogloszenie.id}
|
||||
blur={isOpen ? 'blur' : ''}
|
||||
/>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={`sm:col-span-6 col-span-11 bg-gray-100 sm:h-[84vh] h-[84vh] overflow-y-auto p-4 my-4 ${
|
||||
className={`absolute sm:relative w-full sm:w-[60%] bg-gray-100 sm:h-[89vh] h-[100vh] top-16 sm:top-0 -left-4 sm:left-0 right-0 overflow-y-auto p-8 sm:p-4 border-separate sm:border-l-8 blur-transition ${
|
||||
isDetailsVisible ? "block" : "hidden sm:block"
|
||||
}`}
|
||||
} noblur ${isOpen ? 'blur' : ''}`}
|
||||
>
|
||||
{isDetailsVisible && (
|
||||
<div className="grid grid-cols-6">
|
||||
@ -202,19 +225,8 @@ const WorkApp = () => {
|
||||
)}
|
||||
|
||||
{selectedOgloszenie && (
|
||||
<JobOfferContent id={selectedOgloszenie.id} skills={skills} />
|
||||
<JobOfferContent id={id} skills={skills} />
|
||||
)}
|
||||
{/* <div className='grid sm:grid-cols-2 md:grid-cols-5 grid-cols-2'>
|
||||
{Object.entries(selectedOgloszenie.neededSkills).map(([skill, level]) => (
|
||||
<SkillRender
|
||||
skill={skill}
|
||||
level={level}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className='text-slate-800 font-medium mt-4'>
|
||||
{selectedOgloszenie.tresc}
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@ -169,12 +169,12 @@ export const linki = [
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "work/postings",
|
||||
"name": "work/joboffers",
|
||||
"title": "Ogłoszenia",
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
"name": "work/jobposting",
|
||||
"name": "work/addjoboffer",
|
||||
"title": "Dodaj ogłoszenie",
|
||||
},
|
||||
{
|
||||
@ -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. ",
|
||||
]
|
||||
246
src/index.css
246
src/index.css
@ -22,6 +22,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* @layer components {
|
||||
.no-spin-buttons::-webkit-inner-spin-button,
|
||||
.no-spin-buttons::-webkit-outer-spin-button {
|
||||
@apply -webkit-appearance-none margin-0;
|
||||
}
|
||||
.no-spin-buttons {
|
||||
@apply -moz-appearance-textfield;
|
||||
}
|
||||
} */
|
||||
|
||||
input[type="number"]::-webkit-inner-spin-button,
|
||||
input[type="number"]::-webkit-outer-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Dla Firefox */
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@ -60,11 +81,43 @@
|
||||
}
|
||||
|
||||
/* Expanded state styles */
|
||||
.expanded {
|
||||
max-height: fit-content; /* Adjust as needed */
|
||||
opacity: 1;
|
||||
.collapsible {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition-property: all;
|
||||
transition-timing-function: ease-in-out;
|
||||
transition-duration: 0.5s;
|
||||
}
|
||||
|
||||
/* Styl dla elementu, gdy jest rozwinięty */
|
||||
.collapsible.expanded {
|
||||
max-height: 1000px; /* Przykładowa maksymalna wysokość, może wymagać dostosowania */
|
||||
opacity: 1;
|
||||
transition-property: all;
|
||||
transition-timing-function: ease-in-out;
|
||||
transition-duration: 0.5s;
|
||||
}
|
||||
|
||||
.div-transition {
|
||||
transition-property: filter;
|
||||
transition-duration: 1s;
|
||||
transition-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
.noblur {
|
||||
filter: blur(0px);
|
||||
transition-property: filter;
|
||||
transition-duration: 0.5s;
|
||||
transition-timing-function: ease-in-out;
|
||||
}
|
||||
|
||||
.blur {
|
||||
filter: blur(5px);
|
||||
transition-property: filter;
|
||||
transition-duration: 0.5s;
|
||||
transition-timing-function: ease-in-out;
|
||||
}
|
||||
.minus-z-index {
|
||||
z-index: -10;
|
||||
}
|
||||
@ -101,6 +154,10 @@
|
||||
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;
|
||||
@ -111,57 +168,146 @@
|
||||
animation: slide-down 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
||||
}
|
||||
|
||||
@-webkit-keyframes slide-top {
|
||||
0% {
|
||||
-webkit-transform: translateX(600px);
|
||||
transform: translateX(600px);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
.slider-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@keyframes slide-top {
|
||||
0% {
|
||||
-webkit-transform: translateX(600px);
|
||||
transform: translateX(600px);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes slide-down {
|
||||
0% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translateX(600px);
|
||||
transform: translateX(600px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slide-down {
|
||||
0% {
|
||||
-webkit-transform: translateX(0);
|
||||
transform: translateX(0);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translateX(600px);
|
||||
transform: translateX(600px);
|
||||
}
|
||||
.slider {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.details-section {
|
||||
display: none;
|
||||
}
|
||||
.list-section {
|
||||
display: block;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ module.exports = {
|
||||
colors: {
|
||||
primary: "#00040f",
|
||||
secondary: "#00f6ff",
|
||||
dimWhite: "rgba(255, 255, 255, 0.7)",
|
||||
dimWhite: "rgba(255, 255, 255, 0.8)",
|
||||
dimBlue: "rgba(9, 151, 124, 0.1)",
|
||||
},
|
||||
fontFamily: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user