From 3f8624efd65060d2570237b23f5cc03a43ce0463 Mon Sep 17 00:00:00 2001 From: Jakub K Date: Sun, 17 Mar 2024 19:49:59 +0100 Subject: [PATCH] RC-0.1 --- src/App.jsx | 41 ++--- src/components/AddJobListing.jsx | 41 ++++- src/components/Category.jsx | 18 +++ src/components/EmployerPanel.jsx | 96 +++++++++++ src/components/Filter.jsx | 30 ++++ src/components/ImageUpload.jsx | 4 +- src/components/JobOfferContent.jsx | 65 ++++++++ src/components/JobPosting.jsx | 55 ------- src/components/ListingSmall.jsx | 27 ++++ src/components/Mininav.jsx | 6 +- src/components/Register.jsx | 6 +- src/components/Salary.jsx | 95 +++++------ src/components/Search.jsx | 19 +++ src/components/SelectedSkill.jsx | 23 +-- src/components/SkillList.jsx | 19 +++ src/components/SkillRender.jsx | 45 ++++++ src/components/SkillsSelector.jsx | 170 ++++++++------------ src/components/StepFourJoblisting.jsx | 2 +- src/components/StepThreeJoblisting.jsx | 86 +++++----- src/components/StepTwoJoblisting.jsx | 67 ++++++-- src/components/Success.jsx | 20 +++ src/components/WorkApp.jsx | 150 +++++++++++++---- src/consts/index.js | 212 +++++++++++++++++-------- src/index.css | 31 ++++ vite.config.js | 33 +++- 25 files changed, 952 insertions(+), 409 deletions(-) create mode 100644 src/components/Category.jsx create mode 100644 src/components/EmployerPanel.jsx create mode 100644 src/components/Filter.jsx create mode 100644 src/components/JobOfferContent.jsx delete mode 100644 src/components/JobPosting.jsx create mode 100644 src/components/ListingSmall.jsx create mode 100644 src/components/Search.jsx create mode 100644 src/components/SkillList.jsx create mode 100644 src/components/SkillRender.jsx create mode 100644 src/components/Success.jsx diff --git a/src/App.jsx b/src/App.jsx index c98fc84..9905387 100644 --- a/src/App.jsx +++ b/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 (
- +
@@ -34,15 +38,14 @@ function App() { } /> } /> } /> + } /> {/* Add more routes as needed */}
-
- ) -}; - + ); +} export default App; diff --git a/src/components/AddJobListing.jsx b/src/components/AddJobListing.jsx index d4640a8..e321545 100644 --- a/src/components/AddJobListing.jsx +++ b/src/components/AddJobListing.jsx @@ -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 { return ; case 3: return { removeFields={removeFields} setFormData={setFormData} handleChange={handleChange} - formData={formData} />; + formData={formData} + skills={skills} />; case 4: return ; + formData={formData} + skills={skills} + />; + case 5: + return ; default: return
Unknown step
; } diff --git a/src/components/Category.jsx b/src/components/Category.jsx new file mode 100644 index 0000000..b3cf55d --- /dev/null +++ b/src/components/Category.jsx @@ -0,0 +1,18 @@ +import React from 'react' + +const Category = (props) => { + return ( + + ) +} + +export default Category diff --git a/src/components/EmployerPanel.jsx b/src/components/EmployerPanel.jsx new file mode 100644 index 0000000..29c2569 --- /dev/null +++ b/src/components/EmployerPanel.jsx @@ -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 ( + <> +
+
+

+ Twoje ogłoszenia +

+ {ogloszenia.map((ogloszenie, index) => ( + handleOgloszenieClick(ogloszenie)} + /> + ))} +
+
+ {isDetailsVisible && ( + <> +
+ + + + + {isDetailsVisible && ( + + + + )} +
+
+
+ {Object.entries(selectedOgloszenie.neededSkills).map( + ([skill, level]) => ( + + ) + )} +
+
+ {selectedOgloszenie.tresc} +
+ +
+ + )} +
+
+ + ); +}; + +export default EmployerPanel; diff --git a/src/components/Filter.jsx b/src/components/Filter.jsx new file mode 100644 index 0000000..2643df8 --- /dev/null +++ b/src/components/Filter.jsx @@ -0,0 +1,30 @@ +import React from 'react' +import Search from './Search' + + +const Filter = (props) => { + if (!props.isOpen) return null; + return ( +
+ + + + + + +
+ ) +} + +export default Filter diff --git a/src/components/ImageUpload.jsx b/src/components/ImageUpload.jsx index acef528..b2b9572 100644 --- a/src/components/ImageUpload.jsx +++ b/src/components/ImageUpload.jsx @@ -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) } } diff --git a/src/components/JobOfferContent.jsx b/src/components/JobOfferContent.jsx new file mode 100644 index 0000000..433cc2f --- /dev/null +++ b/src/components/JobOfferContent.jsx @@ -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
; + }; + + useEffect(() => { + fetchJobOfferById(id); + }, [id]); + + return ( + <> + {jobOffer && ( + <> +

{jobOffer.name} w {jobOffer.company_name}

+
+ {jobOffer.skill_levels.map((skillLevel) => { + const skill = skills.find((s) => s.id === parseInt(skillLevel.skill_id)); + return skill ? ( + + ) : null; + })} +
+
{renderContent(jobOffer.content)}
+ + )} + + ); +}; + +export default JobOfferContent; diff --git a/src/components/JobPosting.jsx b/src/components/JobPosting.jsx deleted file mode 100644 index d55bf77..0000000 --- a/src/components/JobPosting.jsx +++ /dev/null @@ -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 ( - <> -
-
- -

Zacznij dodawać ogłoszenie o pracę z Izaac.pl

-
-
- - -
- -
-
-
-
- - -
- { - // 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 ); - } } - /> - - -
- -
- - ) -} - -export default JobPosting diff --git a/src/components/ListingSmall.jsx b/src/components/ListingSmall.jsx new file mode 100644 index 0000000..d41da5e --- /dev/null +++ b/src/components/ListingSmall.jsx @@ -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 ( +
+
+ {/* Obrazek jest wyśrodkowany i zachowuje proporcje */} + {name} +
+
+

{name}

+

{company_name}

+ {/* Wyświetlenie wynagrodzenia pod nazwą, jeśli jest wymagane */} + {require_salary && ( +

{min_salary} - {max_salary} PLN

+ )} +
+
+ ); +}; + +export default ListingSmall; \ No newline at end of file diff --git a/src/components/Mininav.jsx b/src/components/Mininav.jsx index 11790b8..c5d5593 100644 --- a/src/components/Mininav.jsx +++ b/src/components/Mininav.jsx @@ -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 ( <> -
+
{/* Link otwierający modal */} + Panel pracodawcy +
{/* Komponent modalu */} diff --git a/src/components/Register.jsx b/src/components/Register.jsx index c645c71..cf8fab3 100644 --- a/src/components/Register.jsx +++ b/src/components/Register.jsx @@ -25,10 +25,14 @@ const Register = ({isOpen, onClose}) => {
-
+
+
+ + +
@@ -45,7 +49,6 @@ const SelectedSkill = ({ skill, letter, onLevelChange, removeSkillFromList, form

{levelMappings[letter]}

-
); }; diff --git a/src/components/SkillList.jsx b/src/components/SkillList.jsx new file mode 100644 index 0000000..d217d82 --- /dev/null +++ b/src/components/SkillList.jsx @@ -0,0 +1,19 @@ +import React from 'react'; +import SkillRender from './SkillRender'; + + +const SkillsList = ({skillData, skill_names}) => { + return ( +
+ {skillData.map((skillLevel) => { + const skill = skill_names.find((s) => s.id === parseInt(skillLevel.skill_id)); + return skill ? ( + + ) : null; + + })} +
+ ); +}; + +export default SkillsList; diff --git a/src/components/SkillRender.jsx b/src/components/SkillRender.jsx new file mode 100644 index 0000000..384c9f3 --- /dev/null +++ b/src/components/SkillRender.jsx @@ -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) => ( +
+ ))} + + ); +}; + + +const SkillRender = ({key, skill, level,}) => { + + return ( + +
+

{skill}

+
+ +
{renderCircles(level)}
+

{levelMappings[level]}

+
+ + ) +}; + +export default SkillRender; \ No newline at end of file diff --git a/src/components/SkillsSelector.jsx b/src/components/SkillsSelector.jsx index b28c140..ce91a39 100644 --- a/src/components/SkillsSelector.jsx +++ b/src/components/SkillsSelector.jsx @@ -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 ( -
+
-
- + + {inputValue.length >= 1 && suggestions.length > 0 && ( +
6 ? 'h-52' : 'h-min' }`}> + {suggestions.map(suggestion => ( +
handleSuggestionClick(suggestion.skill_name)} + className="w-56 h-max px-3 py-1 bg-slate-300 border-b-2 cursor-pointer" + > +

{suggestion.skill_name}

+
+ ))} +
+ )}
- {inputValue.length >= 1 && suggestions.length > 0 && ( -
- {suggestions.map(suggestion => ( -
handleSuggestionClick(suggestion)} - className="w-56 h-max px-3 py-1 bg-slate-300 border-b-2 cursor-pointer" - > -

{suggestion}

-
- ))} -
- - )} -
- {/* Lista wybranych umiejętności z możliwością określenia poziomu */} +
{selectedSkills.map(skill => ( - level.skill_id === skill.id)?.skill_level || 'N'} + onLevelChange={handleLevelChange} + removeSkillFromList={() => removeSkillFromList(skill)} /> ))} +
); }; -export default SkillsSelector; \ No newline at end of file +export default SkillsSelector; diff --git a/src/components/StepFourJoblisting.jsx b/src/components/StepFourJoblisting.jsx index 4800823..c0a12e4 100644 --- a/src/components/StepFourJoblisting.jsx +++ b/src/components/StepFourJoblisting.jsx @@ -76,7 +76,7 @@ const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
- Nazwa firmy + Nip firmy *
{ +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
; }; - 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 (

Wybrałeś opcję {formData.posting_option}

- -
-
-

F.H.U. Januszex

-

12000 PLN - 15000 PLN

-

Starszy Inżynier ds. ciągłości produkcji

-
-
-

{cleanAndProcessData(formData.company_name)}

- { - formData.requiresalary && (

{cleanAndProcessData(formData.minsalary)} PLN - {cleanAndProcessData(formData.maxsalary)} PLN

) - } - { - !formData.requiresalary && (

) - } -

{cleanAndProcessData(formData.name)}

-
-
-

Papieżeks Sp. z o. o.

-

9000 PLN - 12000 PLN

-

Konstruktor - inżynier mechanik

-
+ {}} + _class={`col-start-2 col-end-4 job-listing`} + /> + {}} + _class={`col-start-2 col-end-4`}/> + {}} + _class={`col-start-2 col-end-4 job-listing2`} />
+ {renderContent(formData.content)}
+
); } diff --git a/src/components/StepTwoJoblisting.jsx b/src/components/StepTwoJoblisting.jsx index a09f1bb..09028dd 100644 --- a/src/components/StepTwoJoblisting.jsx +++ b/src/components/StepTwoJoblisting.jsx @@ -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
-
+
Nazwa ogłoszenia *
@@ -151,10 +157,41 @@ const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setForm
+
Kategoria ogłoszenia i poziom doświadczenia + *
+
+ +

Wybierz kategorię Twojego ogłoszenia

+ +

Poziom doświadczenia

+ +
+
+
+
Wynagrodzenie * diff --git a/src/components/Success.jsx b/src/components/Success.jsx new file mode 100644 index 0000000..9de475b --- /dev/null +++ b/src/components/Success.jsx @@ -0,0 +1,20 @@ +import React from 'react' +import TextDivider from './TextDivider' + +const Success = () => { + return ( + <> + +
+

+ Dziękujemy za dodanie ogłoszenia +

+

+ Twoje ogłoszenie zostanie opublikowane po weryfikacji +

+
+ + ) +} + +export default Success diff --git a/src/components/WorkApp.jsx b/src/components/WorkApp.jsx index 91f2814..b6c1c61 100644 --- a/src/components/WorkApp.jsx +++ b/src/components/WorkApp.jsx @@ -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 (
- {isDetailsVisible && ( -
- -
- )} - -
-
+
+
+ + + + + + + + + + + +
+
+ +
+ +

Oferty pracy

+ + +
+
+ {isOpen && ()} +
{ogloszenia.map((ogloszenie, index) => ( -
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'}`} - > - -

{ogloszenie.company_name}

-

{ogloszenie.salaryRange}

-

{ogloszenie.name}

- -
+ handleOgloszenieClick(ogloszenie)} + /> ))}
-
+ {isDetailsVisible && ( +
+ +
+ )} + + {selectedOgloszenie + && + } + {/*
{Object.entries(selectedOgloszenie.neededSkills).map(([skill, level]) => ( -
-

{skill}

- {renderCircles(level)} -

{level.toLowerCase()}

-
+ ))}
{selectedOgloszenie.tresc} -
+
*/}
@@ -94,3 +175,4 @@ const WorkApp = () => { } export default WorkApp +export { renderCircles } \ No newline at end of file diff --git a/src/consts/index.js b/src/consts/index.js index 54675c3..314d91f 100644 --- a/src/consts/index.js +++ b/src/consts/index.js @@ -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ż", + }, ] \ No newline at end of file diff --git a/src/index.css b/src/index.css index e56a6d4..f9d7ff6 100644 --- a/src/index.css +++ b/src/index.css @@ -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; diff --git a/vite.config.js b/vite.config.js index 5a33944..b8640d7 100644 --- a/vite.config.js +++ b/vite.config.js @@ -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=='); + }); + }, + }, + }, + }, +});