RC-0.1
This commit is contained in:
parent
a5f58d1bdb
commit
3f8624efd6
41
src/App.jsx
41
src/App.jsx
@ -1,26 +1,30 @@
|
||||
import styles from './style';
|
||||
import NavBar from './components/NavBar';
|
||||
import WorkApp from './components/WorkApp';
|
||||
import Home from './components/Home';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import Contact from './components/Contact';
|
||||
import Mininav from './components/Mininav';
|
||||
import AddJobListing from './components/AddJobListing';
|
||||
import styles from "./style";
|
||||
import NavBar from "./components/NavBar";
|
||||
import WorkApp from "./components/WorkApp";
|
||||
import Home from "./components/Home";
|
||||
import { BrowserRouter as Router } from "react-router-dom";
|
||||
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";
|
||||
|
||||
function App() {
|
||||
const loc = () =>{
|
||||
if (location.pathname.startsWith('work/jobposting')){
|
||||
return ""
|
||||
|
||||
const loc = () => {
|
||||
if (location.pathname.startsWith("work/jobposting")) {
|
||||
return "";
|
||||
} else {
|
||||
return "overflow-hidden"
|
||||
return "overflow-hidden";
|
||||
}
|
||||
}
|
||||
};
|
||||
return (
|
||||
<Router>
|
||||
<div className={`bg-white w-full ${loc}`}>
|
||||
<div className={`${styles.paddingX} ${styles.flexCenter} border-b-2`}>
|
||||
<Mininav />
|
||||
<Mininav />
|
||||
<div className={`${styles.boxWidth}`}>
|
||||
<NavBar />
|
||||
</div>
|
||||
@ -34,15 +38,14 @@ function App() {
|
||||
<Route path="/work/postings" element={<WorkApp />} />
|
||||
<Route path="/contact" element={<Contact />} />
|
||||
<Route path="/work/jobposting" element={<AddJobListing />} />
|
||||
<Route path="/employerpanel" element={<EmployerPanel />} />
|
||||
{/* Add more routes as needed */}
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Router>
|
||||
)
|
||||
};
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@ -3,11 +3,15 @@ import StepOneJoblisting from './StepOneJoblisting';
|
||||
import StepTwoJoblisting from './StepTwoJoblisting';
|
||||
import StepThreeJoblisting from './StepThreeJoblisting';
|
||||
import StepFourJoblisting from './StepFourJoblisting'
|
||||
import Success from './Success';
|
||||
import axios from 'axios';
|
||||
|
||||
const AddJobListing = () => {
|
||||
const [currentStep, setCurrentStep] = useState(1)
|
||||
const [skills, setSkills] = useState([])
|
||||
const [formData, setFormData] = useState({
|
||||
'posting_option': 'S',
|
||||
'requiresalary': true
|
||||
'require_salary': true
|
||||
})
|
||||
const nextStep = () => {
|
||||
setCurrentStep(currentStep + 1);
|
||||
@ -47,13 +51,34 @@ const AddJobListing = () => {
|
||||
console.log('Aktualny stan formularza:', formData);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const response = await axios.post('http://izaac.izaac.pl/api/jobposting/joboffers/', formData);
|
||||
console.log('Data posted:', response.data);
|
||||
nextStep();
|
||||
} catch (error) {
|
||||
console.error("Error posting data:", error);
|
||||
}
|
||||
}
|
||||
|
||||
const getSkills = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://izaac.izaac.pl/api/jobposting/skills/');
|
||||
setSkills(response.data.sort((a, b) => a.skill_name.localeCompare(b.skill_name)));
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log('Aktualny stan formularza:', formData);
|
||||
}, [formData]);
|
||||
|
||||
useEffect(() => {
|
||||
getSkills();
|
||||
} , [currentStep])
|
||||
|
||||
switch (currentStep) {
|
||||
case 1:
|
||||
return <StepOneJoblisting
|
||||
@ -64,6 +89,7 @@ const AddJobListing = () => {
|
||||
return <StepFourJoblisting
|
||||
nextStep={nextStep}
|
||||
formData={formData}
|
||||
prevStep={prevStep}
|
||||
handleChange={handleChange} />;
|
||||
case 3:
|
||||
return <StepTwoJoblisting
|
||||
@ -72,12 +98,17 @@ const AddJobListing = () => {
|
||||
removeFields={removeFields}
|
||||
setFormData={setFormData}
|
||||
handleChange={handleChange}
|
||||
formData={formData} />;
|
||||
formData={formData}
|
||||
skills={skills} />;
|
||||
case 4:
|
||||
return <StepThreeJoblisting
|
||||
prevStep={prevStep}
|
||||
handleSubmit={handleSubmit}
|
||||
formData={formData} />;
|
||||
formData={formData}
|
||||
skills={skills}
|
||||
/>;
|
||||
case 5:
|
||||
return <Success />;
|
||||
default:
|
||||
return <div>Unknown step</div>;
|
||||
}
|
||||
|
||||
18
src/components/Category.jsx
Normal file
18
src/components/Category.jsx
Normal file
@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
|
||||
const Category = (props) => {
|
||||
return (
|
||||
<div className='flex flex-shrink-0 items-center gap-6 mr-6'>
|
||||
<a className='mx-auto text-center text-l
|
||||
font-poppins font-semibold text-slate-800
|
||||
py-4 hover:scale-125 duration-200'
|
||||
href='' >
|
||||
{props.name}
|
||||
</a>
|
||||
<div className={`w-0.5 h-6 bg-black opacity-10
|
||||
${props.last ? 'hidden' : 'block'}`}></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Category
|
||||
96
src/components/EmployerPanel.jsx
Normal file
96
src/components/EmployerPanel.jsx
Normal file
@ -0,0 +1,96 @@
|
||||
import React from "react";
|
||||
import { ogloszenia } from "../consts";
|
||||
import ListingSmall from "./ListingSmall";
|
||||
import { useState } from "react";
|
||||
import SkillRender from "./SkillRender";
|
||||
const EmployerPanel = () => {
|
||||
const [showActions, setShowActions] = useState(true);
|
||||
const [selectedOgloszenie, setSelectedOgloszenie] = useState(ogloszenia[0]);
|
||||
const [isDetailsVisible, setIsDetailsVisible] = useState(false); // Dodany stan
|
||||
|
||||
const handleOgloszenieClick = (ogloszenie) => {
|
||||
setSelectedOgloszenie(ogloszenie);
|
||||
setIsDetailsVisible(true); // Pokaż szczegóły na urządzeniach mobilnych
|
||||
};
|
||||
|
||||
// Funkcja do powrotu do listy ogłoszeń
|
||||
const handleBackToList = () => {
|
||||
setIsDetailsVisible(false);
|
||||
};
|
||||
const handleOnClick = () => {
|
||||
setShowActions(showActions);
|
||||
handleOgloszenieClick(selectedOgloszenie);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="px-10 pt-2 grid grid-cols-3 relative">
|
||||
<div className={`md:col-span-1 col-span-3 ${isDetailsVisible ? 'hidden md:block' : 'block'} h-[80vh] overflow-y-auto`}>
|
||||
<p className="sticky top-2 z-50 h-16 text-center font-bold text-[32px] mr-3 mt-2 mb-2 bg-gray-300 rounded-xl text-gray-800 p-2 ">
|
||||
Twoje ogłoszenia
|
||||
</p>
|
||||
{ogloszenia.map((ogloszenie, index) => (
|
||||
<ListingSmall
|
||||
key={ogloszenie.id}
|
||||
name={ogloszenie.name}
|
||||
company_name={ogloszenie.company_name}
|
||||
salaryrange={ogloszenie.salaryRange}
|
||||
requiresalary={ogloszenie.requiresalary}
|
||||
index={index}
|
||||
onClick={() => handleOgloszenieClick(ogloszenie)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="md:col-span-2 col-span-3">
|
||||
{isDetailsVisible && (
|
||||
<>
|
||||
<div className="grid grid-cols-2 place-items-center">
|
||||
<button className="font-bold sm:w-[80%] w-[90%] px-1 sm:h-12 h-16 ml-6 border-2 rounded-xl mt-6 bg-gray-300 hover:bg-gray-400 duration-300">
|
||||
Przedłuż ważność ogłoszenia
|
||||
</button>
|
||||
<button className="font-bold sm:w-[80%] w-[90%] px-1 sm:h-12 h-16 ml-6 border-2 rounded-xl mt-6 bg-gray-300 hover:bg-gray-400 duration-300">
|
||||
Wyróżnij ogłoszenie
|
||||
</button>
|
||||
<button className="font-bold sm:w-[80%] w-[90%] px-1 sm:h-12 h-16 ml-6 border-2 rounded-xl mt-6 bg-gray-300 hover:bg-gray-400 duration-300">
|
||||
Zakończ ogłoszenie
|
||||
</button>
|
||||
<button className="font-bold sm:w-[80%] w-[90%] px-1 sm:h-12 h-16 ml-6 border-2 rounded-xl mt-6 bg-gray-300 hover:bg-gray-400 duration-300">
|
||||
Dodaj nowe ogłoszenie
|
||||
</button>
|
||||
{isDetailsVisible && (
|
||||
|
||||
<button
|
||||
onClick={handleBackToList}
|
||||
className="md:hidden block font-poppins text-white font-semibold bg-gray-500 px-4 py-2 rounded-md mx-4 mt-6 w-[90%] col-span-2 place-self-center"
|
||||
>
|
||||
Powrót do listy
|
||||
</button>
|
||||
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={`bg-gray-200 md:h-[68vh] h-[95%] overflow-y-auto p-4 my-4 ${
|
||||
isDetailsVisible ? "block" : "hidden md:block"
|
||||
}`}
|
||||
>
|
||||
<div className="grid md:grid-cols-5 sm:grid-cols-4 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>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EmployerPanel;
|
||||
30
src/components/Filter.jsx
Normal file
30
src/components/Filter.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import React from 'react'
|
||||
import Search from './Search'
|
||||
|
||||
|
||||
const Filter = (props) => {
|
||||
if (!props.isOpen) return null;
|
||||
return (
|
||||
<div className={`sticky top-24 z-20 bg-gray-100 grid grid-cols-2 slide-container my-6 py-5 ${!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ą...' />
|
||||
|
||||
<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>
|
||||
)
|
||||
}
|
||||
|
||||
export default Filter
|
||||
@ -6,6 +6,8 @@ import axios from 'axios';
|
||||
const ImageUpload = ({setFormData, data}) => {
|
||||
const [imageSrc, setImageSrc] = useState(placeholderImage);
|
||||
const fileInputRef = useRef(); // Referencja do ukrytego inputu plików
|
||||
const [uploadedImageData, setUploadedImageData] = useState(null);
|
||||
|
||||
|
||||
const handleImageChange = async (e) => {
|
||||
const file = e.target.files[0];
|
||||
@ -38,7 +40,7 @@ const ImageUpload = ({setFormData, data}) => {
|
||||
setImageSrc(url)
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Błąd podczas przesyłania obrazka:', e)
|
||||
console.error('Błąd podczas przesyłania obrazka:', error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
65
src/components/JobOfferContent.jsx
Normal file
65
src/components/JobOfferContent.jsx
Normal file
@ -0,0 +1,65 @@
|
||||
import React, { 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
|
||||
|
||||
const JobOfferContent = ({ id, skills }) => {
|
||||
const [jobOffer, setJobOffer] = useState(null);
|
||||
|
||||
const fetchJobOfferById = async (jobId) => {
|
||||
try {
|
||||
const response = await axios.get(`http://izaac.izaac.pl/api/jobposting/joboffers/${jobId}`);
|
||||
setJobOffer(response.data);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch job offer:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const processHTML = (htmlString) => {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(htmlString, 'text/html');
|
||||
doc.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach((tag) => {
|
||||
tag.classList.add('no-tailwindcss-base', 'text-xl');
|
||||
});
|
||||
doc.querySelectorAll('p').forEach((tag) => {
|
||||
tag.classList.add('no-tailwindcss-base', 'text-slate-800', 'text-lg');
|
||||
});
|
||||
doc.querySelectorAll('ol, ul').forEach((tag) => tag.classList.add('no-tailwindcss-base'));
|
||||
doc.querySelectorAll('li').forEach((tag) => tag.classList.add('lista'));
|
||||
return doc.body.innerHTML;
|
||||
};
|
||||
|
||||
const cleanAndProcessData = (userInput) => {
|
||||
const sanitizedHTML = DOMPurify.sanitize(userInput, { USE_PROFILES: { html: true } });
|
||||
return processHTML(sanitizedHTML);
|
||||
};
|
||||
|
||||
const renderContent = (htmlString) => {
|
||||
return <div dangerouslySetInnerHTML={{ __html: cleanAndProcessData(htmlString) }} />;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchJobOfferById(id);
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{jobOffer && (
|
||||
<>
|
||||
<h1 className="my-4 mx-6 text-3xl font-bold text-slate-800">{jobOffer.name} w {jobOffer.company_name}</h1>
|
||||
<div className="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{jobOffer.skill_levels.map((skillLevel) => {
|
||||
const skill = skills.find((s) => s.id === parseInt(skillLevel.skill_id));
|
||||
return skill ? (
|
||||
<SkillRender key={skill.id} skill={skill.skill_name} level={skillLevel.skill_level} />
|
||||
) : null;
|
||||
})}
|
||||
</div>
|
||||
<div className="text-slate-800 font-medium mt-4">{renderContent(jobOffer.content)}</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobOfferContent;
|
||||
@ -1,55 +0,0 @@
|
||||
import React from 'react'
|
||||
import styles from '../style'
|
||||
import { CKEditor } from '@ckeditor/ckeditor5-react';
|
||||
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
|
||||
const JobPosting = () => {
|
||||
return (
|
||||
<>
|
||||
<div className='grid grid-cols-3'>
|
||||
<div className='sm:px-20 px-16 sm:pt-12 pt-6 pb-6 xl:col-span-2 col-span-3'>
|
||||
<span className='justify-center'>
|
||||
<p className={`${styles.paragraph}`}>Zacznij dodawać ogłoszenie o pracę z Izaac.pl </p>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div className='no-tailwindcss-base'>
|
||||
<div className='editor-container max-w-none h-[70vh]'>
|
||||
<form action="">
|
||||
<div className='grid mb-4'>
|
||||
<label className={`${styles.paragraph} px-4`} htmlFor="">Nazwa ogłoszenia</label>
|
||||
<input type="text" name="" id="" placeholder='Wpisz nazwę stanowiska...' className={`border-b-2 px-4 my-3 mx-2 ${styles.paragraph} `}/>
|
||||
</div>
|
||||
<CKEditor
|
||||
editor={ ClassicEditor }
|
||||
className=""
|
||||
config={{}}
|
||||
data="<h1>Zacznij pisać ogłoszenie!</h1>"
|
||||
onReady={ editor => {
|
||||
// You can store the "editor" and use when it is needed.
|
||||
console.log( 'Editor is ready to use!', editor );
|
||||
} }
|
||||
onChange={ ( event, editor ) => {
|
||||
const data = editor.getData();
|
||||
console.log( { event, editor, data } );
|
||||
} }
|
||||
onBlur={ ( event, editor ) => {
|
||||
console.log( 'Blur.', editor );
|
||||
} }
|
||||
onFocus={ ( event, editor ) => {
|
||||
console.log( 'Focus.', editor );
|
||||
} }
|
||||
/>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default JobPosting
|
||||
27
src/components/ListingSmall.jsx
Normal file
27
src/components/ListingSmall.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react'
|
||||
import { IzaacLOGO } from '../assets'
|
||||
|
||||
const ListingSmall = ({ id, name, company_name, min_salary, max_salary, require_salary, index, onClick, _class, image }) => {
|
||||
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}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className='flex-shrink-0 w-[6rem] h-[6rem] bg-white rounded-lg flex items-center justify-center'>
|
||||
{/* Obrazek jest wyśrodkowany i zachowuje proporcje */}
|
||||
<img src={image || IzaacLOGO} alt={name} className='object-contain object-center p-2' />
|
||||
</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>
|
||||
{/* 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>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListingSmall;
|
||||
@ -1,6 +1,8 @@
|
||||
import React, { 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)
|
||||
@ -13,14 +15,16 @@ const Mininav = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='bg-gray-500 h-5 absolute w-full top-0 flex justify-end items-center'>
|
||||
<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>
|
||||
<button onClick={openPopup} className='text-white font-poppins font-semibold text-xs mx-12'>
|
||||
Rejestracja
|
||||
</button>
|
||||
<button onClick={openLogin} className='text-white font-poppins font-semibold text-xs sm:mr-12 mr-6 sm:pr-12'>
|
||||
Zaloguj się
|
||||
</button>
|
||||
|
||||
</div>
|
||||
{/* Komponent modalu */}
|
||||
<Register isOpen={isPopupOpen} onClose={closePopup} />
|
||||
|
||||
@ -25,10 +25,14 @@ const Register = ({isOpen, onClose}) => {
|
||||
<div className='mb-6 pr-4'>
|
||||
<input type="password" name="" id="" placeholder='Powtórz hasło' className={`border-b px-4 my-3 mx-2 ${styles.paragraph} w-full text-center `}/>
|
||||
</div>
|
||||
<div className='mb-6 pr-4'>
|
||||
<div className='pr-4'>
|
||||
<input type="checkbox" name="" id="" placeholder='' className={`border-b-2 px-4 my-3 mx-2 ${styles.paragraph} text-center `}/>
|
||||
<label>Zapoznałem sie z <a href='#' className='hover:text-blue-600 font-bold sm'>regulaminem portalu Izaac.pl</a> i go akceptuję</label>
|
||||
</div>
|
||||
<div className='mb-6 pr-4'>
|
||||
<input type="checkbox" name="" id="" placeholder='' className={`border-b-2 px-4 my-3 mx-2 ${styles.paragraph} text-center `}/>
|
||||
<label>Rejestruję się jako pracodawca</label>
|
||||
</div>
|
||||
|
||||
<button type="submit" className="bg-gray-700 text-white px-4 py-2 rounded w-full font-semibold text">
|
||||
Z a r e j e s t r u j s i ę
|
||||
|
||||
@ -1,35 +1,24 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import styles from '../style'
|
||||
const EmploymentTypes = [
|
||||
'Kontrakt B2B',
|
||||
'Umowa o pracę',
|
||||
'Umowa zlecenie',
|
||||
'Umowa o dzieło',
|
||||
'Staż',
|
||||
]
|
||||
const WorkFromHomes = [
|
||||
'Praca zdalna',
|
||||
'Hybrydowo',
|
||||
'W biurze',
|
||||
'Inne'
|
||||
]
|
||||
|
||||
import {employment_types, work_from_home } from '../consts'
|
||||
|
||||
const Salary = ({handleChange, formData, removeFields, setFormData}) => {
|
||||
const [requireSalary, setRequireSalary] = useState(true)
|
||||
const [require_salary, setrequire_salary] = useState(true)
|
||||
|
||||
const handleRequireSalary = () => {
|
||||
setRequireSalary(prevRequireSalary => !prevRequireSalary);
|
||||
const handlerequire_salary = () => {
|
||||
setrequire_salary(prevrequire_salary => !prevrequire_salary);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setFormData({...formData, 'requiresalary': requireSalary});
|
||||
}, [requireSalary]);
|
||||
setFormData({...formData, 'require_salary': require_salary});
|
||||
}, [require_salary]);
|
||||
|
||||
const [minBigger, setMinBigger] = useState(true);
|
||||
const handleCheck = () => {
|
||||
let minSalary = parseInt(formData.minSalary);
|
||||
let maxSalary = parseInt(formData.maxSalary);
|
||||
if (minSalary > maxSalary) {
|
||||
let min_salary = parseInt(formData.min_salary);
|
||||
let max_salary = parseInt(formData.max_salary);
|
||||
if (min_salary > max_salary) {
|
||||
setMinBigger(true)
|
||||
}
|
||||
else {
|
||||
@ -39,29 +28,29 @@ const Salary = ({handleChange, formData, removeFields, setFormData}) => {
|
||||
useEffect(() => {
|
||||
handleCheck();
|
||||
console.log(minBigger)
|
||||
console.log(formData.minSalary)
|
||||
console.log(formData.maxSalary)
|
||||
console.log(formData.min_salary)
|
||||
console.log(formData.max_salary)
|
||||
|
||||
}, [formData.minSalary, formData.maxSalary]);
|
||||
}, [formData.min_salary, formData.max_salary]);
|
||||
useEffect(() => {
|
||||
})
|
||||
useEffect( () =>{
|
||||
if (formData.requiresalary === false) {
|
||||
removeFields(['minsalary', 'maxsalary']);
|
||||
setRequireSalary(formData.requiresalary)
|
||||
if (formData.require_salary === false) {
|
||||
removeFields(['min_salary', 'max_salary']);
|
||||
setrequire_salary(formData.require_salary)
|
||||
}
|
||||
}, [formData.requiresalary])
|
||||
}, [formData.require_salary])
|
||||
|
||||
|
||||
return (
|
||||
<div className='grid grid-cols-4 mt-4 gap-6'>
|
||||
<div className={`grid grid-cols-4 mt-4 gap-6 ${minBigger ? 'mb-10' : ''} duration-300`}>
|
||||
<div className='col-span-4'>
|
||||
<input
|
||||
className='ml-6'
|
||||
type="checkbox"
|
||||
checked={!requireSalary}
|
||||
name="requiresalary"
|
||||
onChange={handleRequireSalary}
|
||||
checked={!require_salary}
|
||||
name="require_salary"
|
||||
onChange={handlerequire_salary}
|
||||
id="" />
|
||||
<span className='font-poppins font-semibold text-slate-700 text-center text-[14px] mt-1 mb-2'> Nie chce podawać wysokości wynagrodzenia</span>
|
||||
</div>
|
||||
@ -69,53 +58,53 @@ const Salary = ({handleChange, formData, removeFields, setFormData}) => {
|
||||
<div className='relative'>
|
||||
<p className='ml-5 font-poppins font-semibold text-slate-700 text-center text-[14px] mt-1 mb-2'>Min. wynagrodzenie</p>
|
||||
<input
|
||||
disabled={!requireSalary}
|
||||
name='minsalary'
|
||||
value={formData['minsalary'] || '' }
|
||||
disabled={!require_salary}
|
||||
name='min_salary'
|
||||
value={formData['min_salary'] || '' }
|
||||
|
||||
onChange={handleChange('minsalary')}
|
||||
onChange={handleChange('min_salary')}
|
||||
type='number'
|
||||
className={`h-12 border-2 rounded-xl px-5 mx-4 w-full ${!requireSalary ? 'bg-slate-200' : ''}`}/>
|
||||
className={`h-12 border-2 rounded-xl px-5 mx-4 w-full ${!require_salary ? 'bg-slate-200' : ''}`}/>
|
||||
{minBigger && (
|
||||
<div className='absolute top-[5rem] left-8 border-2 border-red-500 bg-red-200 rounded-b-xl p-2 opacity-90' >
|
||||
<p className='font-poppins text-red-700 text-[14px] mt-1 mb-1'>Minimalne wynagrodzenie nie może być większe od maks. wynagrodzenia</p>
|
||||
<p className='font-poppins text-red-700 text-[11px] mt-1 mb-1'>Minimalne wynagrodzenie nie może być większe od maks. wynagrodzenia</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div >
|
||||
<p className='ml-5 font-poppins font-semibold text-slate-700 text-center text-[14px] mt-1 mb-2'>Maks. wynagrodzenie</p>
|
||||
<input
|
||||
disabled={!requireSalary}
|
||||
value={formData['maxsalary'] || '' }
|
||||
name="maxSalary"
|
||||
onChange={handleChange('maxsalary')}
|
||||
disabled={!require_salary}
|
||||
value={formData['max_salary'] || '' }
|
||||
name="max_salary"
|
||||
onChange={handleChange('max_salary')}
|
||||
type='number'
|
||||
className={`h-12 border-2 rounded-xl px-5 mx-4 w-full ${!requireSalary ? 'bg-slate-200' : ''} `}/></div>
|
||||
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>
|
||||
<select
|
||||
value={formData['employmentType'] || 'default' }
|
||||
value={formData['employment_type'] || 'default' }
|
||||
name='employmentType'
|
||||
onChange={handleChange('employmentType')}
|
||||
onChange={handleChange('employment_type')}
|
||||
className='h-12 border-2 rounded-xl px-5 mx-4 w-full '>
|
||||
<option value={'default'} disabled >Wybierz opcję</option>
|
||||
{EmploymentTypes.map(EmploymentType => (
|
||||
<option key={EmploymentType}>{EmploymentType}</option>
|
||||
{employment_types.map(employment_type => (
|
||||
<option key={employment_type.id} value={employment_type.id}>{employment_type.name}</option>
|
||||
))}
|
||||
|
||||
</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 zdalna</p>
|
||||
<select
|
||||
value={formData['workFromHome'] || 'default' }
|
||||
value={formData['work_from_home'] || 'default' }
|
||||
name='workFromHome'
|
||||
onChange={handleChange('workFromHome')}
|
||||
onChange={handleChange('work_from_home')}
|
||||
className='h-12 border-2 rounded-xl px-5 mx-4 w-full'>
|
||||
<option value={'default'} disabled >Wybierz opcję</option>
|
||||
|
||||
{WorkFromHomes.map(WorkFromHomes => (
|
||||
<option key={WorkFromHomes}>{WorkFromHomes}</option>
|
||||
{work_from_home.map(work_from_home => (
|
||||
<option key={work_from_home.id} value={work_from_home.id}>{work_from_home.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
19
src/components/Search.jsx
Normal file
19
src/components/Search.jsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react'
|
||||
|
||||
const Search = (props) => {
|
||||
return (
|
||||
<div className='grid py-2'>
|
||||
<label className="mx-2 text-l font- text-center font-poppins"
|
||||
htmlFor="search">
|
||||
{props.label}
|
||||
</label>
|
||||
<input type="text"
|
||||
placeholder={props.placeholder}
|
||||
className="border-2 border-gray-300
|
||||
bg-white h-10 px-5 pr-16 mx-3 rounded-lg
|
||||
text-sm focus:outline-none" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Search
|
||||
@ -7,6 +7,7 @@ const levelMappings = {
|
||||
'A': 'Zaawansowany',
|
||||
'E': 'Ekspert',
|
||||
};
|
||||
const skillLevels = ['N', 'B', 'M', 'A', 'E'];
|
||||
|
||||
function renderCircles(letter, handleCircleClick) {
|
||||
const level = levelMappings[letter];
|
||||
@ -17,7 +18,7 @@ function renderCircles(letter, handleCircleClick) {
|
||||
{[...Array(5)].map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => handleCircleClick(Object.keys(levelMappings)[index])}
|
||||
onClick={() => handleCircleClick(skillLevels[index])}
|
||||
className={`justify-self-center w-3.5 h-3.5 rounded-full
|
||||
${index < numberOfFilledCircles ? 'bg-red-300 hover:bg-red-700' : 'bg-gray-500 hover:bg-gray-600'}`}
|
||||
/>
|
||||
@ -27,16 +28,19 @@ function renderCircles(letter, handleCircleClick) {
|
||||
};
|
||||
|
||||
|
||||
const SelectedSkill = ({ skill, letter, onLevelChange, removeSkillFromList, formData }) => {
|
||||
const handleCircleClick = (levelIndex) => {
|
||||
const letter = Object.keys(levelMappings)[levelIndex];
|
||||
onLevelChange(skill, levelIndex);
|
||||
const SelectedSkill = ({ skill_name, skillId ,letter, onLevelChange, removeSkillFromList }) => {
|
||||
const handleCircleClick = (newLetter) => {
|
||||
// const letter = Object.keys(levelMappings)[levelIndex];
|
||||
console.log(`Circle clicked: ${newLetter}`);
|
||||
onLevelChange(skillId, newLetter);
|
||||
};
|
||||
return (
|
||||
<div className="skill-block">
|
||||
<div key={skill} className="relative selected-skill bg-slate-200 rounded-2xl h-20 w-56 p-2 mb-5">
|
||||
<p className='text-slate-700 font-semibold text-[12px] text-center'>{skill}</p>
|
||||
<button onClick={(e) => {e.preventDefault(); removeSkillFromList(skill)}} className="absolute top-1 right-5 text-black">
|
||||
<div className="mr-12">
|
||||
<div key={skillId} className="relative selected-skill bg-slate-200 rounded-2xl h-min w-full p-2 mb-5 m-2 ">
|
||||
<div className='flex justify-center items-center'>
|
||||
<span className='text-slate-700 font-semibold text-[12px] text-center w-48 '>{skill_name}</span>
|
||||
</div>
|
||||
<button onClick={(e) => {e.preventDefault(); removeSkillFromList(skill_name)}} className="absolute top-1 right-5 text-black">
|
||||
×
|
||||
</button>
|
||||
<div className='h-0.5 w-full bg-dimWhite opacity-60 mt-1'></div>
|
||||
@ -45,7 +49,6 @@ const SelectedSkill = ({ skill, letter, onLevelChange, removeSkillFromList, form
|
||||
</div>
|
||||
<p className='font-poppins font-semibold text-slate-700 text-center text-[12px] mt-1'>{levelMappings[letter]}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
19
src/components/SkillList.jsx
Normal file
19
src/components/SkillList.jsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import SkillRender from './SkillRender';
|
||||
|
||||
|
||||
const SkillsList = ({skillData, skill_names}) => {
|
||||
return (
|
||||
<div className='grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3'>
|
||||
{skillData.map((skillLevel) => {
|
||||
const skill = skill_names.find((s) => s.id === parseInt(skillLevel.skill_id));
|
||||
return skill ? (
|
||||
<SkillRender key={skill.id} skill={skill.skill_name} level={skillLevel.skill_level} />
|
||||
) : null;
|
||||
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SkillsList;
|
||||
45
src/components/SkillRender.jsx
Normal file
45
src/components/SkillRender.jsx
Normal file
@ -0,0 +1,45 @@
|
||||
import React from 'react'
|
||||
|
||||
|
||||
const levelMappings = {
|
||||
'N': 'Nice to have',
|
||||
'B': 'Podstawowy',
|
||||
'M': 'Średnio zaawansowany',
|
||||
'A': 'Zaawansowany',
|
||||
'E': 'Ekspert',
|
||||
};
|
||||
|
||||
function renderCircles(letter) {
|
||||
const level = levelMappings[letter];
|
||||
const numberOfFilledCircles = Object.keys(levelMappings).indexOf(letter) + 1;
|
||||
|
||||
return (
|
||||
<>
|
||||
{[...Array(5)].map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`justify-self-center w-3.5 h-3.5 rounded-full
|
||||
${index < numberOfFilledCircles ? 'bg-red-300 hover:bg-red-700' : 'bg-gray-500 hover:bg-gray-600'}`}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
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}>
|
||||
<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>
|
||||
|
||||
<div className='mt-3 grid grid-cols-5'>{renderCircles(level)}</div>
|
||||
<p className='mt-3 text-xs text-center font-bold'>{levelMappings[level]}</p>
|
||||
</div>
|
||||
|
||||
)
|
||||
};
|
||||
|
||||
export default SkillRender;
|
||||
@ -1,142 +1,104 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import SelectedSkill from './SelectedSkill';
|
||||
const initialSkillsList = [
|
||||
'Inżynieria oprogramowania',
|
||||
'Analiza danych',
|
||||
'Projektowanie CAD',
|
||||
'Automatyka',
|
||||
'Robotyka',
|
||||
'Inżynieria materiałowa',
|
||||
'Inżynieria elektryczna',
|
||||
'Inżynieria mechaniczna',
|
||||
'Programowanie mikrokontrolerów',
|
||||
'Analiza strukturalna',
|
||||
'Modelowanie 3D',
|
||||
'Projektowanie układów elektronicznych',
|
||||
'Programowanie PLC',
|
||||
'Sztuczna inteligencja',
|
||||
'Machine learning',
|
||||
'Inżynieria chemiczna',
|
||||
'Inżynieria biomedyczna',
|
||||
'Inżynieria środowiska',
|
||||
'Automatyka przemysłowa',
|
||||
'Zarządzanie projektami technicznymi',
|
||||
'Prototypowanie',
|
||||
'Przetwarzanie sygnałów',
|
||||
'Inżynieria systemów',
|
||||
'Analiza termodynamiczna',
|
||||
'Fizyka stosowana',
|
||||
'Inżynieria procesowa',
|
||||
'Wytwarzanie addytywne',
|
||||
'Technologia nanomateriałów',
|
||||
'Projektowanie systemów wbudowanych',
|
||||
'Inżynieria bezpieczeństwa',
|
||||
];
|
||||
import axios from 'axios';
|
||||
|
||||
const SkillsSelector = ({formData, setFormData}) => {
|
||||
const SkillsSelector = ({ formData, setFormData, skills }) => {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [suggestions, setSuggestions] = useState([]);
|
||||
const [selectedSkills, setSelectedSkills] = useState([]);
|
||||
const [skill_levels, setskill_levels] = useState([]);
|
||||
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const value = e.target.value;
|
||||
setInputValue(value);
|
||||
if (value.length >= 0) {
|
||||
setSuggestions(initialSkillsList.filter(skill =>
|
||||
skill.toLowerCase().includes(value.toLowerCase())));
|
||||
if (value.length > 0) {
|
||||
setSuggestions(skills.filter(skill =>
|
||||
skill.skill_name.toLowerCase().includes(value.toLowerCase())
|
||||
));
|
||||
} else {
|
||||
setSuggestions([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSuggestionClick = (skill) => {
|
||||
if (!selectedSkills.includes(skill)) {
|
||||
setSelectedSkills(prevSkills => [skill, ...prevSkills]);
|
||||
};
|
||||
setSkillLevels(prevLevels => ({
|
||||
...prevLevels,
|
||||
[skill]: 'N' // lub inny domyślny poziom
|
||||
}));
|
||||
// Handle click on suggestion
|
||||
const handleSuggestionClick = (suggestion) => {
|
||||
const skill = skills.find(skill => skill.skill_name === suggestion);
|
||||
|
||||
if (skill && !selectedSkills.some(selectedSkill => selectedSkill.id === skill.id)) {
|
||||
setSelectedSkills(prevSkills => [...prevSkills, skill]);
|
||||
setskill_levels(prevLevels => [...prevLevels, { skill_id: skill.id, skill_level: 'N' }]);
|
||||
}
|
||||
|
||||
setInputValue('');
|
||||
setSuggestions([]);
|
||||
};
|
||||
|
||||
// Handle removal of a skill from the list
|
||||
const removeSkillFromList = (skillToRemove) => {
|
||||
setSelectedSkills(prevSkills => prevSkills.filter(skill => skill !== skillToRemove));
|
||||
|
||||
setSkillLevels(prevLevels => {
|
||||
const newLevels = { ...prevLevels };
|
||||
delete newLevels[skillToRemove];
|
||||
return newLevels;
|
||||
});
|
||||
};
|
||||
|
||||
const [skillLevels, setSkillLevels] = useState({});
|
||||
const handleLevelChange = (skill, newLevel) => {
|
||||
setSkillLevels(prevLevels => ({ ...prevLevels, [skill]: newLevel }));
|
||||
setSelectedSkills(prevSkills => prevSkills.filter(skill => skill.id !== skillToRemove.id));
|
||||
|
||||
setskill_levels(prevLevels => prevLevels.filter(level => level.skill_id !== skillToRemove.id));
|
||||
};
|
||||
|
||||
// Handle skill level change
|
||||
const handleLevelChange = (skillId, newLevel) => {
|
||||
setskill_levels(prevLevels =>
|
||||
prevLevels.map(level =>
|
||||
level.skill_id === skillId ? { ...level, skill_level: newLevel } : level
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// Update formData on skill_levels change
|
||||
useEffect(() => {
|
||||
// Aktualizacja formData, aby zawierała skillLevels
|
||||
setFormData(prevFormData => ({
|
||||
...prevFormData,
|
||||
skillLevels: skillLevels
|
||||
skill_levels
|
||||
}));
|
||||
}, [skillLevels]);
|
||||
|
||||
useEffect(() => {
|
||||
// Jeśli formData zawiera już wybrane umiejętności i ich poziomy
|
||||
if (formData.skillLevels) {
|
||||
setSkillLevels(formData.skillLevels);
|
||||
// Ustaw wybrane umiejętności na podstawie kluczy w formData.skillLevels
|
||||
setSelectedSkills(Object.keys(formData.skillLevels));
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('Zaktualizowane poziomy umiejętności:', skillLevels);
|
||||
}, [skillLevels, ]);
|
||||
}, [skill_levels, setFormData]);
|
||||
|
||||
return (
|
||||
<div className='relative mt-3 grid grid-cols-5'>
|
||||
<div className='relative mt-3 grid grid-cols-2'>
|
||||
<div className='col-span-5 grid mb-4'>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Wpisz umiejętność"
|
||||
className="selected-skill bg-slate-200 rounded-2xl h-20 w-56 py-2 px-4 text-slate-700 font-semibold text-[12px] col-span-3"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Wpisz umiejętność..."
|
||||
className="selected-skill bg-slate-200 rounded-2xl h-20 w-full
|
||||
sm:w-56 py-2 px-4 text-slate-700 font-semibold text-[16px]
|
||||
col-span-3"
|
||||
/>
|
||||
{inputValue.length >= 1 && suggestions.length > 0 && (
|
||||
<div className={`absolute border-4 w-56 top-16 z-10 overflow-y-auto overflow-x-hidden ${suggestions.length > 6 ? 'h-52' : 'h-min' }`}>
|
||||
{suggestions.map(suggestion => (
|
||||
<div
|
||||
key={suggestion.id}
|
||||
onClick={() => handleSuggestionClick(suggestion.skill_name)}
|
||||
className="w-56 h-max px-3 py-1 bg-slate-300 border-b-2 cursor-pointer"
|
||||
>
|
||||
<p className='text-slate-700 font-semibold text-[12px]'>{suggestion.skill_name}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{inputValue.length >= 1 && suggestions.length > 0 && (
|
||||
<div className="absolute border-4 w-56 top-16 z-10 overflow-y-auto overflow-x-hidden m" >
|
||||
{suggestions.map(suggestion => (
|
||||
<div
|
||||
key={suggestion}
|
||||
onClick={() => handleSuggestionClick(suggestion)}
|
||||
className="w-56 h-max px-3 py-1 bg-slate-300 border-b-2 cursor-pointer"
|
||||
>
|
||||
<p className='text-slate-700 font-semibold text-[12px]'> {suggestion}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
)}
|
||||
</div>
|
||||
{/* Lista wybranych umiejętności z możliwością określenia poziomu */}
|
||||
<div className='col-span-5 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3'>
|
||||
{selectedSkills.map(skill => (
|
||||
|
||||
<SelectedSkill
|
||||
key={skill}
|
||||
skill={skill}
|
||||
letter={skillLevels[skill] || 'N'} // Domyślny poziom, jeśli nie ustawiony
|
||||
onLevelChange={handleLevelChange}
|
||||
removeSkillFromList={removeSkillFromList}
|
||||
key={skill.id}
|
||||
skill_name={skill.skill_name}
|
||||
skillId={skill.id}
|
||||
letter={skill_levels.find(
|
||||
level => level.skill_id === skill.id)?.skill_level || 'N'}
|
||||
onLevelChange={handleLevelChange}
|
||||
removeSkillFromList={() => removeSkillFromList(skill)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SkillsSelector;
|
||||
export default SkillsSelector;
|
||||
|
||||
@ -76,7 +76,7 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
|
||||
</div>
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
Nazwa firmy
|
||||
Nip firmy
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span>
|
||||
</div>
|
||||
<input
|
||||
|
||||
@ -2,8 +2,12 @@ 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'
|
||||
|
||||
const StepThreeJoblisting = ({ formData, prevStep }) => {
|
||||
import SkillsList from './SkillList';
|
||||
|
||||
const StepThreeJoblisting = ({ formData, prevStep, skills, handleSubmit }) => {
|
||||
// Funkcja do przetwarzania HTML i dodawania klas
|
||||
const processHTML = (htmlString) => {
|
||||
const parser = new DOMParser();
|
||||
@ -33,62 +37,68 @@ const StepThreeJoblisting = ({ formData, prevStep }) => {
|
||||
return <div dangerouslySetInnerHTML={{ __html: cleanAndProcessData(htmlString) }} />;
|
||||
};
|
||||
|
||||
const skillData = Object.entries(formData.skillLevels).map(([skill_id, skill_level]) => {
|
||||
return {skill_id, skill_level, jobposting_id: null};
|
||||
});
|
||||
|
||||
useEffect(()=>{
|
||||
console.log(skillData)
|
||||
}, [skillData,])
|
||||
|
||||
return (
|
||||
<div className='text-center'>
|
||||
<p className='mt-8 text-[24px] font-poppins font-bold '>
|
||||
Wybrałeś opcję {formData.posting_option}
|
||||
</p>
|
||||
|
||||
<TextDivider text={`Tak będzie wyglądało Twoje ogłoszenie na liście ogłoszeń`} />
|
||||
|
||||
<div className={`grid grid-cols-4`}>
|
||||
<div
|
||||
className={`job-listing col-start-2 col-end-4 grid grid-cols-4 drop-shadow cursor-pointer mb-2 sm:mr-4 mr-0 text-xl font-bold border rounded-[10px] p-2 hover:border-l-8 hover:border-zinc-500 duration-300 bg-gray-200`}
|
||||
>
|
||||
<p className='col-span-3 text-xl text-left tracking-wide'>F.H.U. Januszex</p>
|
||||
<p className='place-self-end text-[16px] font-semibold tracking-widest text-slate-800'>12000 PLN - 15000 PLN</p>
|
||||
<p className='col-span-4 text-xl text-left tracking-wide'>Starszy Inżynier ds. ciągłości produkcji</p>
|
||||
</div>
|
||||
<div
|
||||
className={`col-start-2 col-end-4 grid grid-cols-4 drop-shadow cursor-pointer mb-2 sm:mr-4 mr-0 text-xl font-bold border rounded-[10px] p-2 hover:border-l-8 hover:border-zinc-500 duration-300 bg-gray-200`}
|
||||
>
|
||||
<p className='col-span-3 text-base text-left tracking-wide'>{cleanAndProcessData(formData.company_name)}</p>
|
||||
{
|
||||
formData.requiresalary && (<p className='place-self-end text-[16px] font-semibold tracking-widest text-slate-800'>{cleanAndProcessData(formData.minsalary)} PLN - {cleanAndProcessData(formData.maxsalary)} PLN</p> )
|
||||
}
|
||||
{
|
||||
!formData.requiresalary && (<p className='place-self-end text-xl text-slate-800'></p> )
|
||||
}
|
||||
<p className='col-span-4 text-xl text-left'>{cleanAndProcessData(formData.name)}</p>
|
||||
</div>
|
||||
<div
|
||||
className={`job-listing2 col-start-2 col-end-4 grid grid-cols-4 drop-shadow cursor-pointer mb-2 sm:mr-4 mr-0 text-xl font-bold border rounded-[10px] p-2 hover:border-l-8 hover:border-zinc-500 duration-300 bg-gray-200`}
|
||||
>
|
||||
<p className='col-span-3 text-xl text-left tracking-wide'>Papieżeks Sp. z o. o.</p>
|
||||
<p className='place-self-end text-[16px] font-semibold tracking-widest text-slate-800'>9000 PLN - 12000 PLN</p>
|
||||
<p className='col-span-4 text-xl text-left tracking-wide'>Konstruktor - inżynier mechanik</p>
|
||||
</div>
|
||||
<ListingSmall
|
||||
id={9999}
|
||||
name={'Starszy Inżynier ds. ciągłości produkcji'}
|
||||
company_name={'F.H.U. Januszex'}
|
||||
min_salary={'12000'}
|
||||
max_salary={'15000 PLN'}
|
||||
image={IzaacLOGO}
|
||||
|
||||
require_salary={true}
|
||||
index={0}
|
||||
onClick={() => {}}
|
||||
_class={`col-start-2 col-end-4 job-listing`}
|
||||
/>
|
||||
<ListingSmall
|
||||
id={999999}
|
||||
name={formData.name}
|
||||
company_name={formData.company_name}
|
||||
min_salary={formData.min_salary}
|
||||
max_salary={formData.max_salary}
|
||||
requiresalary={formData.require_salary}
|
||||
image={formData.image}
|
||||
index={0}
|
||||
onClick={() => {}}
|
||||
_class={`col-start-2 col-end-4`}/>
|
||||
<ListingSmall
|
||||
id={99999}
|
||||
name={'Konstruktor - inżynier mechanik'}
|
||||
company_name={'Papieżeks Sp. z o. o.'}
|
||||
image={IzaacLOGO}
|
||||
min_salary={'9000'}
|
||||
max_salary={'12000'}
|
||||
requiresalary={true}
|
||||
index={1}
|
||||
onClick={() => {}}
|
||||
_class={`col-start-2 col-end-4 job-listing2`} />
|
||||
</div>
|
||||
<TextDivider text={`Tak będzie się prezentowała treść Twojego ogłoszenia`} />
|
||||
<div className='grid grid-cols-5 mt-4 mb-4'>
|
||||
<div className='col-span-3 col-start-2 bg-gray-100 text-justify text-[18px] leading-[1.75rem] p-4 rounded-xl'>
|
||||
<SkillsList skillData={formData.skill_levels} skill_names={skills}/>
|
||||
{renderContent(formData.content)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={prevStep}
|
||||
className='h-12 w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] text-white hover:scale-125 duration-300 mb-5'>
|
||||
className='h-12 w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] text-white hover:scale-125 duration-300 mb-5 mx-12'>
|
||||
<span className='text-[18px]'>←</span> Przejdź do poprzedniego kroku
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleSubmit}
|
||||
className='h-12 w-72 rounded-xl bg-green-700 font-poppins font-semibold text-[14px] text-white hover:scale-125 duration-300 mb-5 mx-12'>
|
||||
Dodaj ogłoszenie! <span className='text-[18px]'>→</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,9 +5,13 @@ import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
import ImageUpload from './ImageUpload';
|
||||
import SkillsSelector from './SkillsSelector';
|
||||
import Salary from './Salary';
|
||||
import { placeholderImage } from '../assets';
|
||||
|
||||
const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setFormData, removeFields }) => {
|
||||
import { categories, experience_levels } from '../consts';
|
||||
|
||||
const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setFormData, removeFields, skills }) => {
|
||||
const [editorData, setEditorData] = useState('');
|
||||
const [imageSrc, setImageSrc] = useState(placeholderImage);
|
||||
const handleEditorChange = (event, editor) => {
|
||||
const data = editor.getData();
|
||||
setEditorData(data);
|
||||
@ -39,29 +43,29 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm
|
||||
if (!formData.content) {
|
||||
newErrors.content = 'To pole jest wymagane';
|
||||
}
|
||||
console.log(formData.requireSalary)
|
||||
if (formData.requireSalary === true)
|
||||
console.log(formData.require_salary)
|
||||
if (formData.require_salary === true)
|
||||
{
|
||||
console.log(formData.requireSalary)
|
||||
if (!formData.minsalary) {
|
||||
newErrors.minsalary = 'To pole jest wymagane';
|
||||
console.log(formData.require_salary)
|
||||
if (!formData.min_salary) {
|
||||
newErrors.min_salary = 'To pole jest wymagane';
|
||||
}
|
||||
if (!formData.maxsalary) {
|
||||
newErrors.maxsalary = 'To pole jest wymagane';
|
||||
if (!formData.max_salary) {
|
||||
newErrors.max_salary = 'To pole jest wymagane';
|
||||
}
|
||||
}
|
||||
|
||||
if (!formData.workFromHome) {
|
||||
newErrors.workFromHome = 'To pole jest wymagane';
|
||||
if (!formData.work_from_home) {
|
||||
newErrors.work_from_home = 'To pole jest wymagane';
|
||||
}
|
||||
if (!formData.employmentType) {
|
||||
newErrors.employmentType = 'To pole jest wymagane';
|
||||
if (!formData.employment_type) {
|
||||
newErrors.employment_type = 'To pole jest wymagane';
|
||||
}
|
||||
if (!formData.image) {
|
||||
newErrors.image = 'To pole jest wymagane';
|
||||
}
|
||||
if (Object.keys(formData.skillLevels).length === 0) {
|
||||
newErrors.skillLevels = 'To pole jest wymagane';
|
||||
if (Object.keys(formData.skill_levels).length === 0) {
|
||||
newErrors.skill_levels = 'To pole jest wymagane';
|
||||
}
|
||||
setErrors(newErrors);
|
||||
|
||||
@ -82,7 +86,7 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm
|
||||
<div className='mx-auto max-w-none h-[70vh] w-[60vw]'>
|
||||
<form action="">
|
||||
<div className='grid mb-4'>
|
||||
<div className='grid grid-cols-3 items-center'>
|
||||
<div className='grid grid-cols-1 sm:grid-cols-3 items-center'>
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>Nazwa ogłoszenia
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span></div>
|
||||
<input type="text"
|
||||
@ -119,6 +123,8 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm
|
||||
<ImageUpload
|
||||
setFormData={setFormData}
|
||||
data={formData}
|
||||
_setImageSrc={setImageSrc}
|
||||
_imageSrc={imageSrc}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -151,10 +157,41 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm
|
||||
<SkillsSelector
|
||||
formData={formData}
|
||||
setFormData={setFormData}
|
||||
skills={skills}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full border-b-2'></div>
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>Kategoria ogłoszenia i poziom doświadczenia
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span></div>
|
||||
<div className='grid grid-cols-2 items-center mt-4 mb-4 gap-6'>
|
||||
|
||||
<p className='font-poppins font-semibold text-slate-700 text-center text-[14px] mt-1 mb-2'>Wybierz kategorię Twojego ogłoszenia</p>
|
||||
<select
|
||||
value={formData['category'] || 'default' }
|
||||
name='category'
|
||||
onChange={handleChange('category')}
|
||||
className='h-12 border-2 rounded-xl px-5 mx-4 w-full '>
|
||||
<option value={'default'} disabled >Wybierz opcję</option>
|
||||
{categories.map((category, index) => (
|
||||
<option key={index} value={category.id}>{category.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<p className='font-poppins font-semibold text-slate-700 text-center text-[14px] mt-1 mb-2'>Poziom doświadczenia</p>
|
||||
<select
|
||||
value={formData['experience_level'] || 'default' }
|
||||
name='experience_level'
|
||||
onChange={handleChange('experience_level')}
|
||||
className='h-12 border-2 rounded-xl px-5 mx-4 w-full '>
|
||||
<option value={'default'} disabled >Wybierz opcję</option>
|
||||
{experience_levels.map((experience_level, index) => (
|
||||
<option key={index} value={experience_level.id}>{experience_level.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full border-b-2'></div>
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
Wynagrodzenie
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span>
|
||||
|
||||
20
src/components/Success.jsx
Normal file
20
src/components/Success.jsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import TextDivider from './TextDivider'
|
||||
|
||||
const Success = () => {
|
||||
return (
|
||||
<>
|
||||
<TextDivider text={`Twoje ogłoszenie zostało dodane!`} />
|
||||
<div className='text-center'>
|
||||
<p className='mt-8 text-[24px] font-poppins font-bold '>
|
||||
Dziękujemy za dodanie ogłoszenia
|
||||
</p>
|
||||
<p className='mt-8 text-[16px] font-poppins font-bold '>
|
||||
Twoje ogłoszenie zostanie opublikowane po weryfikacji
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Success
|
||||
@ -1,7 +1,14 @@
|
||||
import React from 'react'
|
||||
import { ogloszenia } from '../consts'
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import ListingSmall from './ListingSmall';
|
||||
import SkillRender from './SkillRender';
|
||||
import Search from './Search';
|
||||
import Filter from './Filter';
|
||||
import Category from './Category';
|
||||
import JobOfferContent from './JobOfferContent';
|
||||
|
||||
import axios from 'axios';
|
||||
function renderCircles(level) {
|
||||
let numberOfFilledCircles;
|
||||
|
||||
@ -32,8 +39,49 @@ function renderCircles(level) {
|
||||
};
|
||||
|
||||
const WorkApp = () => {
|
||||
const [isDetailsVisible, setIsDetailsVisible] = useState(false);
|
||||
// Czy szczegóły są widoczne
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
// Funkcja pobierająca dane z API
|
||||
const [ogloszenia, setOgloszenia] = useState([]);
|
||||
|
||||
const [skills, setSkills] = useState([]);
|
||||
|
||||
const getOgloszenia = async () => {
|
||||
try {
|
||||
const response = await axios.get('http://izaac.izaac.pl/api/jobposting/joboffers_list/');
|
||||
const data = response.data;
|
||||
setOgloszenia(data);
|
||||
// console.log(data);
|
||||
if (data.length > 0) {
|
||||
setSelectedOgloszenie(data[0]);
|
||||
// console.log(data[0])
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const getSkills = async () => {
|
||||
try {
|
||||
const response = await axios.get(`http://izaac.izaac.pl/api/jobposting/skills/`);
|
||||
const data = response.data;
|
||||
setSkills(data);
|
||||
console.log(data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedOgloszenie, setSelectedOgloszenie] = useState(ogloszenia[0]);
|
||||
const [isDetailsVisible, setIsDetailsVisible] = useState(false); // Dodany stan
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
getOgloszenia();
|
||||
getSkills();
|
||||
}, []);
|
||||
|
||||
const handleOgloszenieClick = (ogloszenie) => {
|
||||
setSelectedOgloszenie(ogloszenie);
|
||||
@ -46,47 +94,80 @@ const WorkApp = () => {
|
||||
};
|
||||
return (
|
||||
<section className=' mx-auto bg-white w-full'>
|
||||
{isDetailsVisible && (
|
||||
<div className='grid grid-cols-6'>
|
||||
<button
|
||||
onClick={handleBackToList}
|
||||
className="sm:hidden block font-poppins text-white font-semibold bg-gray-500 px-4 py-2 rounded-md mx-4 col-span-4 col-start-2"
|
||||
>
|
||||
Powrót do listy
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className='flex-grow h-5/6 grid grid-cols-10 '>
|
||||
|
||||
<div className={`rounded-xl col-span-11 sm:col-span-4 bg-gray-100 overflow-y-auto p-2 my-4 mx-2 sm:h-[84vh] h-[84vh] ${isDetailsVisible ? 'hidden sm:block' : 'block'}`}>
|
||||
<div className='flex-grow h-5/6 grid grid-cols-10'>
|
||||
<div className='px-3 col-span-11 flex flex-wrap items-center place-content-center '>
|
||||
|
||||
|
||||
<Category name='Budownictwo' />
|
||||
<Category name='Elektryk' />
|
||||
<Category name='Okretownictwo' />
|
||||
<Category name='Energetyka' />
|
||||
<Category name='Lorem Ipsum' />
|
||||
<Category name='IT'/>
|
||||
<Category name='Budownictwo'
|
||||
last={true} />
|
||||
|
||||
|
||||
</div>
|
||||
<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'}`}>
|
||||
|
||||
<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'>Oferty pracy</h1>
|
||||
<button
|
||||
className='block font-poppins text-white font-semibold
|
||||
bg-gray-500 px-4 py-2 mx-6 rounded-md'
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
>Pokaż 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>
|
||||
{ogloszenia.map((ogloszenie, index) => (
|
||||
<div
|
||||
key={ogloszenie.id}
|
||||
onClick={() => handleOgloszenieClick(ogloszenie)}
|
||||
className={`grid grid-cols-4 drop-shadow cursor-pointer mb-2 sm:mr-4 mr-0 text-xl font-bold border rounded-[10px] p-2 hover:border-l-8 hover:border-zinc-500 duration-300 ${index % 2 === 0 ? 'bg-gray-200' : 'bg-gray-300'}`}
|
||||
>
|
||||
|
||||
<p className='col-span-3 text-sm'>{ogloszenie.company_name}</p>
|
||||
<p className='place-self-end text-sm text-slate-800'>{ogloszenie.salaryRange}</p>
|
||||
<p className='col-span-4'>{ogloszenie.name}</p>
|
||||
|
||||
</div>
|
||||
<ListingSmall
|
||||
key={ogloszenie.id}
|
||||
name={ogloszenie.name}
|
||||
company_name={ogloszenie.company_name}
|
||||
min_salary={ogloszenie.min_salary}
|
||||
max_salary={ogloszenie.max_salary}
|
||||
require_salary={ogloszenie.require_salary}
|
||||
image={ogloszenie.image}
|
||||
index={index}
|
||||
onClick={() => handleOgloszenieClick(ogloszenie)}
|
||||
/>
|
||||
))}
|
||||
</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 ${isDetailsVisible ? 'block' : 'hidden sm:block'}`}>
|
||||
<div className='grid sm:grid-cols-2 md:grid-cols-5 grid-cols-2'>
|
||||
{isDetailsVisible && (
|
||||
<div className='grid grid-cols-6'>
|
||||
<button
|
||||
onClick={handleBackToList}
|
||||
className="sm:hidden block font-poppins text-white font-semibold bg-gray-500 px-4 py-2 rounded-md mx-4 col-span-4 col-start-2"
|
||||
>
|
||||
Powrót do listy
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedOgloszenie
|
||||
&&
|
||||
<JobOfferContent
|
||||
id={selectedOgloszenie.id}
|
||||
skills={skills}
|
||||
/>}
|
||||
{/* <div className='grid sm:grid-cols-2 md:grid-cols-5 grid-cols-2'>
|
||||
{Object.entries(selectedOgloszenie.neededSkills).map(([skill, level]) => (
|
||||
<div className='col-span-1 w-36 h-30 rounded-[15px] bg-slate-300 py-4 pl-3 m-2 hover:bg-slate-200' key={skill}>
|
||||
<p className='font-bold mb-1 '>{skill}</p>
|
||||
{renderCircles(level)}
|
||||
<p className='text-xs font-bold'>{level.toLowerCase()}</p>
|
||||
</div>
|
||||
<SkillRender
|
||||
skill={skill}
|
||||
level={level}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className='text-slate-800 font-medium mt-4'>
|
||||
{selectedOgloszenie.tresc}
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -94,3 +175,4 @@ const WorkApp = () => {
|
||||
}
|
||||
|
||||
export default WorkApp
|
||||
export { renderCircles }
|
||||
@ -13,7 +13,8 @@ export const ogloszenia = [
|
||||
"Coś innego": "Zaawansowany",
|
||||
"Papieżworks": "Podstawowy",
|
||||
"Budowa papieży": "Ekspert",
|
||||
}
|
||||
},
|
||||
"requiresalary": true
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
@ -26,7 +27,8 @@ export const ogloszenia = [
|
||||
"Elektronika": "Ekspert",
|
||||
"PLC": "Zaawansowany",
|
||||
"Automatyka przemysłowa": "Podstawowy"
|
||||
}
|
||||
},
|
||||
"requiresalary": false
|
||||
},
|
||||
{
|
||||
"id": "3",
|
||||
@ -39,7 +41,8 @@ export const ogloszenia = [
|
||||
"AutoCAD": "Ekspert",
|
||||
"Zarządzanie projektem": "Zaawansowany",
|
||||
"Nadzór budowlany": "Zaawansowany"
|
||||
}
|
||||
},
|
||||
"requiresalary": true
|
||||
},
|
||||
{
|
||||
"id": "4",
|
||||
@ -52,7 +55,8 @@ export const ogloszenia = [
|
||||
"AutoCAD": "Zaawansowany",
|
||||
"SolidWorks": "Podstawowy",
|
||||
"Budowa statków": "Ekspert"
|
||||
}
|
||||
},
|
||||
"requiresalary": true
|
||||
},
|
||||
{
|
||||
"id": "5",
|
||||
@ -65,7 +69,8 @@ export const ogloszenia = [
|
||||
"Elektronika": "Ekspert",
|
||||
" PLC": "Zaawansowany",
|
||||
"Automatyka przemysłowa": "Podstawowy"
|
||||
}
|
||||
},
|
||||
"requiresalary": true
|
||||
},
|
||||
{
|
||||
"id": "6",
|
||||
@ -78,7 +83,8 @@ export const ogloszenia = [
|
||||
"AutoCAD": "Ekspert",
|
||||
"Zarządzanie projektem": "Zaawansowany",
|
||||
"Nadzór budowlany": "Zaawansowany"
|
||||
}
|
||||
},
|
||||
"requiresalary": true
|
||||
},
|
||||
{
|
||||
"id": "7",
|
||||
@ -91,7 +97,8 @@ export const ogloszenia = [
|
||||
"AutoCAD": "Zaawansowany",
|
||||
"SolidWorks": "Podstawowy",
|
||||
"Budowa statków": "Ekspert"
|
||||
}
|
||||
},
|
||||
"requiresalary": true
|
||||
},
|
||||
{
|
||||
"id": "8",
|
||||
@ -102,10 +109,12 @@ export const ogloszenia = [
|
||||
"lokalizacja": "Gdańsk, Poland",
|
||||
"neededSkills": {
|
||||
"Elektronika": "Ekspert",
|
||||
" PLC": "Zaawansowany",
|
||||
"PLC": "Zaawansowany",
|
||||
"Automatyka przemysłowa": "Podstawowy"
|
||||
}
|
||||
},
|
||||
},
|
||||
"requiresalary": true
|
||||
}
|
||||
,
|
||||
{
|
||||
"id": "9",
|
||||
"company_name": "Kompania S. A",
|
||||
@ -117,7 +126,8 @@ export const ogloszenia = [
|
||||
"AutoCAD": "Ekspert",
|
||||
"Zarządzanie projektem": "Zaawansowany",
|
||||
"Nadzór budowlany": "Zaawansowany"
|
||||
}
|
||||
},
|
||||
"requiresalary": true
|
||||
},
|
||||
{
|
||||
"id": "10",
|
||||
@ -130,7 +140,8 @@ export const ogloszenia = [
|
||||
"AutoCAD": "Zaawansowany",
|
||||
"SolidWorks": "Podstawowy",
|
||||
"Budowa statków": "Ekspert"
|
||||
}
|
||||
},
|
||||
"requiresalary": true
|
||||
},
|
||||
{
|
||||
"id": "11",
|
||||
@ -141,64 +152,13 @@ export const ogloszenia = [
|
||||
"lokalizacja": "Gdańsk, Poland",
|
||||
"neededSkills": {
|
||||
"Elektronika": "Ekspert",
|
||||
" PLC": "Zaawansowany",
|
||||
"PLC": "Zaawansowany",
|
||||
"Automatyka przemysłowa": "Podstawowy"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "12",
|
||||
"company_name": "Kompania S. A",
|
||||
"name": "Inżynier budowlany",
|
||||
"tresc": "Zapraszamy do aplikowania osoby z doświadczeniem w nadzorowaniu i zarządzaniu dużymi projektami budowlanymi.",
|
||||
"salaryRange": "14,000 - 22,000 PLN",
|
||||
"lokalizacja": "Gdańsk, Poland",
|
||||
"neededSkills": {
|
||||
"AutoCAD": "Ekspert",
|
||||
"Zarządzanie projektem": "Zaawansowany",
|
||||
"Nadzór budowlany": "Zaawansowany"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "13",
|
||||
"company_name": "Kompania S. A",
|
||||
"name": "Inżynier mechanik w branży okrętowniczej",
|
||||
"tresc": "Poszukujemy doświadczonego inżyniera mechanika specjalizującego się w budowie i konserwacji statków. Wymagane minimum 5 lat doświadczenia w branży okrętowniczej.",
|
||||
"salaryRange": "10,000 - 15,000 PLN",
|
||||
"lokalizacja": "Gdańsk, Poland",
|
||||
"neededSkills": {
|
||||
"AutoCAD": "Zaawansowany",
|
||||
"SolidWorks": "Podstawowy",
|
||||
"Budowa statków": "Ekspert"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "14",
|
||||
"company_name": "Kompania S. A",
|
||||
"name": "Inżynier elektryk w branży energetycznej",
|
||||
"tresc": "Szukamy inżyniera elektryka z doświadczeniem w projektowaniu i implementacji systemów energetycznych.",
|
||||
"salaryRange": "12,000 - 18,000 PLN",
|
||||
"lokalizacja": "Gdańsk, Poland",
|
||||
"neededSkills": {
|
||||
"Elektronika": "Ekspert",
|
||||
" PLC": "Zaawansowany",
|
||||
"Automatyka przemysłowa": "Podstawowy"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "15",
|
||||
"company_name": "Kompania S. A",
|
||||
"name": "Inżynier budowlany",
|
||||
"tresc": "Zapraszamy do aplikowania osoby z doświadczeniem w nadzorowaniu i zarządzaniu dużymi projektami budowlanymi.",
|
||||
"salaryRange": "14,000 - 22,000 PLN",
|
||||
"lokalizacja": "Gdańsk, Poland",
|
||||
"neededSkills": {
|
||||
"AutoCAD": "Ekspert",
|
||||
"Zarządzanie projektem": "Zaawansowany",
|
||||
"Nadzór budowlany": "Zaawansowany"
|
||||
}
|
||||
},
|
||||
//... Możesz dodać więcej ogłoszeń według tego wzoru
|
||||
},
|
||||
"requiresalary": true
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
|
||||
export const linki = [
|
||||
@ -250,4 +210,120 @@ export const linki_home = [
|
||||
"name": "contact",
|
||||
"title": "Kontakt",
|
||||
},
|
||||
]
|
||||
|
||||
export const categories = [
|
||||
{
|
||||
"id": "A",
|
||||
"name": "Budownictwo",
|
||||
},
|
||||
{
|
||||
"id": "B",
|
||||
"name": "IT",
|
||||
},
|
||||
{
|
||||
"id": "C",
|
||||
"name": "Elektryka i Elektronika",
|
||||
},
|
||||
{
|
||||
"id": "D",
|
||||
"name": "Produkcja",
|
||||
},
|
||||
{
|
||||
"id": "E",
|
||||
"name": "Mechanika i konstrukcje",
|
||||
},
|
||||
{
|
||||
"id": "F",
|
||||
"name": "Chemia i Biotechnologia",
|
||||
},
|
||||
{
|
||||
"id": "G",
|
||||
"name": "Biomedyczne",
|
||||
},
|
||||
{
|
||||
"id": "H",
|
||||
"name": "Automatyka i Robotyka",
|
||||
},
|
||||
{
|
||||
"id": "I",
|
||||
"name": "Logistyka i Transport",
|
||||
},
|
||||
{
|
||||
"id": "J",
|
||||
"name": "Sprzedaż",
|
||||
},
|
||||
{
|
||||
"id": "Z",
|
||||
"name": "Inne",
|
||||
},
|
||||
]
|
||||
|
||||
export const experience_levels = [
|
||||
{
|
||||
"id": "A",
|
||||
"name": "Stażysta",
|
||||
},
|
||||
{
|
||||
"id": "B",
|
||||
"name": "Junior",
|
||||
},
|
||||
{
|
||||
"id": "C",
|
||||
"name": "Mid",
|
||||
},
|
||||
{
|
||||
"id": "D",
|
||||
"name": "Senior",
|
||||
},
|
||||
{
|
||||
"id": "E",
|
||||
"name": "Lead",
|
||||
},
|
||||
{
|
||||
"id": "F",
|
||||
"name": "Manager",
|
||||
},
|
||||
{
|
||||
"id": "G",
|
||||
"name": "Inne",
|
||||
},
|
||||
]
|
||||
|
||||
export const work_from_home = [
|
||||
{
|
||||
"id": "wfh",
|
||||
"name": "Praca zdalna",
|
||||
},
|
||||
{
|
||||
"id": "hyb",
|
||||
"name": "Hybrydowa",
|
||||
},
|
||||
{
|
||||
"id": "off",
|
||||
"name": "Stacjonarna",
|
||||
},
|
||||
]
|
||||
|
||||
export const employment_types = [
|
||||
{
|
||||
"id": "B2B",
|
||||
"name": "Kontrakt B2B",
|
||||
},
|
||||
{
|
||||
"id": "FT",
|
||||
"name": "Umowa o pracę",
|
||||
},
|
||||
{
|
||||
"id": "MC",
|
||||
"name": "Umowa zlecenie",
|
||||
},
|
||||
{
|
||||
"id": "CW",
|
||||
"name": "Umowa o dzieło",
|
||||
},
|
||||
{
|
||||
"id": "INT",
|
||||
"name": "Staż",
|
||||
},
|
||||
]
|
||||
@ -25,6 +25,14 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
#root {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
||||
|
||||
:root {
|
||||
--black-gradient: linear-gradient(
|
||||
@ -38,6 +46,29 @@
|
||||
* {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
.category-hover:hover {
|
||||
transform: scale(1.1);
|
||||
/* Apply negative margin if needed */
|
||||
}
|
||||
|
||||
.slide-container {
|
||||
overflow: hidden;
|
||||
max-height: 0;
|
||||
transition: max-height 0.5s ease-out, opacity 0.5s ease;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* Expanded state styles */
|
||||
.expanded {
|
||||
max-height: fit-content; /* Adjust as needed */
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.minus-z-index {
|
||||
z-index: -10;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
@ -1,7 +1,32 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
// Dodaj to jeśli korzystasz z HTTPS i potrzebujesz ignorować błędy certyfikatu
|
||||
// import https from 'https';
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
server: {
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://izaac.izaac.pl',
|
||||
changeOrigin: false,
|
||||
secure: false, // Ustaw na true jeśli łączysz się przez HTTPS
|
||||
// Jeśli twoje połączenie HTTPS wymaga niestandardowego certyfikatu:
|
||||
// https: {
|
||||
// agent: new https.Agent({
|
||||
// rejectUnauthorized: false,
|
||||
// }),
|
||||
// },
|
||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
configure: (proxy, options) => {
|
||||
// Funkcja konfigurująca, gdzie możesz dodać dodatkowe nagłówki
|
||||
proxy.on('proxyReq', function(proxyReq, req) {
|
||||
// Dodaj tutaj swój nagłówek autoryzacyjny
|
||||
proxyReq.setHeader('Authorization', 'Basic cnJnTkxTRXFsY2w0NVJWTVFhMEx4UUxPSE9nWjJMN1psR3BYVXJDcDpsS3NtcmFlU21paFk4clRRZ2Q4VFRHSW5jVWxvVzdYb2tSOFdMSTBvWnE4akNKNTlndUFrb3BnOVpPVWVYRmR5cnF3dUxpNlR5WExaSkRwMUtCOERXRXVzMDV0dFMzTlFIb0x2ZlJvT1F0SnBRYXh6eUlvODVveWtqUW4yNUtlYg==');
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user