komponenty
@ -3,7 +3,9 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<link href="/dist/output.css" rel="stylesheet">
|
||||
<link href="/dist/output.css" rel="stylesheet" />
|
||||
<link rel="icon" href="../icon/favicon.ico"/>
|
||||
<!-- todo ICONs -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>izaac frontend</title>
|
||||
</head>
|
||||
|
||||
6
package-lock.json
generated
@ -11,6 +11,7 @@
|
||||
"@ckeditor/ckeditor5-build-classic": "^40.0.0",
|
||||
"@ckeditor/ckeditor5-build-multi-root": "^40.0.0",
|
||||
"@ckeditor/ckeditor5-react": "^6.1.0",
|
||||
"dompurify": "^3.0.6",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.18.0"
|
||||
@ -2069,6 +2070,11 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.6.tgz",
|
||||
"integrity": "sha512-ilkD8YEnnGh1zJ240uJsW7AzE+2qpbOUYjacomn3AvJ6J4JhKGSZ2nh4wUIXPZrEPppaCLx5jFe8T89Rk8tQ7w=="
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.574",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.574.tgz",
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
"@ckeditor/ckeditor5-build-classic": "^40.0.0",
|
||||
"@ckeditor/ckeditor5-build-multi-root": "^40.0.0",
|
||||
"@ckeditor/ckeditor5-react": "^6.1.0",
|
||||
"dompurify": "^3.0.6",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.18.0"
|
||||
|
||||
22
src/App.jsx
@ -5,29 +5,35 @@ 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 JobPosting from './components/JobPosting';
|
||||
import Mininav from './components/Mininav';
|
||||
import Register from './components/Register';
|
||||
import AddJobListing from './components/AddJobListing';
|
||||
|
||||
function App() {
|
||||
const loc = () =>{
|
||||
if (location.pathname.startsWith('work/jobposting')){
|
||||
return ""
|
||||
} else {
|
||||
return "overflow-hidden"
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Router>
|
||||
<div className='bg-white w-full overflow-hidden'>
|
||||
<div className={`${styles.paddingX} ${styles.flexCenter}`}>
|
||||
<Mininav />
|
||||
<div className={`bg-white w-full ${loc}`}>
|
||||
<div className={`${styles.paddingX} ${styles.flexCenter} border-b-2`}>
|
||||
<Mininav />
|
||||
<div className={`${styles.boxWidth}`}>
|
||||
<NavBar />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.flexStart}`}>
|
||||
<div className={`${styles.boxWidth2} h-full`}>
|
||||
<div className={`${styles.boxWidth2} `}>
|
||||
<Routes>
|
||||
<Route path="/home" element={<Home />} />
|
||||
<Route path="/work" element={<WorkApp />} />
|
||||
<Route path="/work/jobpostings" element={<WorkApp />} />
|
||||
<Route path="/work/postings" element={<WorkApp />} />
|
||||
<Route path="/contact" element={<Contact />} />
|
||||
<Route path="/work/jobposting" element={<JobPosting />} />
|
||||
<Route path="/work/jobposting" element={<AddJobListing />} />
|
||||
{/* Add more routes as needed */}
|
||||
</Routes>
|
||||
</div>
|
||||
|
||||
@ -2,9 +2,11 @@ import close from './close.svg'
|
||||
import menu from './menu.svg'
|
||||
import search from './search.svg'
|
||||
import IzaacLOGO from './IzaacLOGO.svg'
|
||||
import placeholderImage from './placeholderImage.svg'
|
||||
export {
|
||||
close,
|
||||
menu,
|
||||
search,
|
||||
IzaacLOGO
|
||||
IzaacLOGO,
|
||||
placeholderImage
|
||||
}
|
||||
23
src/assets/placeholderImage.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="800.000000pt" height="600.000000pt" viewBox="0 0 800.000000 600.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.16, written by Peter Selinger 2001-2019
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,600.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M2780 3873 c-40 -14 -40 -4 -40 -883 0 -808 1 -848 18 -863 17 -16
|
||||
118 -17 1254 -17 1029 0 1237 2 1247 14 8 10 10 246 9 872 l-3 858 -25 13
|
||||
c-20 10 -278 13 -1235 12 -665 0 -1217 -3 -1225 -6z m2390 -878 l0 -775 -1160
|
||||
0 -1160 0 0 775 0 775 1160 0 1160 0 0 -775z"/>
|
||||
<path d="M3075 3596 c-64 -33 -97 -81 -103 -151 -12 -144 129 -245 260 -187
|
||||
67 29 101 80 106 157 5 77 -20 133 -77 170 -58 39 -124 43 -186 11z"/>
|
||||
<path d="M4208 3330 c-14 -10 -129 -161 -257 -335 -128 -173 -233 -315 -235
|
||||
-315 -2 0 -39 48 -82 108 -103 141 -126 162 -175 162 -31 0 -47 -7 -73 -32
|
||||
-31 -30 -476 -631 -476 -643 0 -3 493 -5 1096 -5 931 0 1095 2 1091 14 -3 8
|
||||
-173 242 -378 521 -226 307 -385 515 -404 526 -39 24 -76 24 -107 -1z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
87
src/components/AddJobListing.jsx
Normal file
@ -0,0 +1,87 @@
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import StepOneJoblisting from './StepOneJoblisting';
|
||||
import StepTwoJoblisting from './StepTwoJoblisting';
|
||||
import StepThreeJoblisting from './StepThreeJoblisting';
|
||||
import StepFourJoblisting from './StepFourJoblisting'
|
||||
const AddJobListing = () => {
|
||||
const [currentStep, setCurrentStep] = useState(1)
|
||||
const [formData, setFormData] = useState({
|
||||
'postingOption': 'Opcja 2',
|
||||
'requireSalary': true
|
||||
})
|
||||
const nextStep = () => {
|
||||
setCurrentStep(currentStep + 1);
|
||||
};
|
||||
|
||||
const prevStep = () => {
|
||||
setCurrentStep(currentStep - 1);
|
||||
};
|
||||
const removeFields = (fieldNames) => {
|
||||
// Utwórz nowy obiekt, który początkowo jest kopią formData
|
||||
let newFormData = { ...formData };
|
||||
|
||||
// Iteruj przez każde pole do usunięcia
|
||||
fieldNames.forEach(fieldName => {
|
||||
// Usuń pole z nowego obiektu danych
|
||||
delete newFormData[fieldName];
|
||||
});
|
||||
|
||||
// Aktualizuj stan z nowym obiektem
|
||||
setFormData(newFormData);
|
||||
};
|
||||
|
||||
const handleChange = input => value => {
|
||||
// Sprawdzenie, czy 'value' jest obiektem zdarzenia
|
||||
const isEvent = value && value.target;
|
||||
let newValue;
|
||||
|
||||
if (isEvent) {
|
||||
// Jeśli 'value' jest obiektem zdarzenia, użyj jego właściwości 'value'
|
||||
newValue = value.target.value;
|
||||
} else {
|
||||
// W przeciwnym razie użyj 'value' bezpośrednio
|
||||
newValue = value;
|
||||
}
|
||||
|
||||
setFormData({ ...formData, [input]: newValue });
|
||||
console.log('Aktualny stan formularza:', formData);
|
||||
};
|
||||
|
||||
const handleSubmit = () => {
|
||||
|
||||
}
|
||||
useEffect(() => {
|
||||
console.log('Aktualny stan formularza:', formData);
|
||||
}, [formData]);
|
||||
|
||||
switch (currentStep) {
|
||||
case 1:
|
||||
return <StepOneJoblisting
|
||||
nextStep={nextStep}
|
||||
handleChange={handleChange}
|
||||
formData={formData} />;
|
||||
case 2:
|
||||
return <StepFourJoblisting
|
||||
nextStep={nextStep}
|
||||
formData={formData}
|
||||
handleChange={handleChange} />;
|
||||
case 3:
|
||||
return <StepTwoJoblisting
|
||||
nextStep={nextStep}
|
||||
prevStep={prevStep}
|
||||
removeFields={removeFields}
|
||||
setFormData={setFormData}
|
||||
handleChange={handleChange}
|
||||
formData={formData} />;
|
||||
case 4:
|
||||
return <StepThreeJoblisting
|
||||
prevStep={prevStep}
|
||||
handleSubmit={handleSubmit}
|
||||
formData={formData} />;
|
||||
default:
|
||||
return <div>Unknown step</div>;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export default AddJobListing;
|
||||
62
src/components/ImageUpload.jsx
Normal file
@ -0,0 +1,62 @@
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { IzaacLOGO, placeholderImage } from '../assets'
|
||||
|
||||
const ImageUpload = ({setFormData}) => {
|
||||
const [imageSrc, setImageSrc] = useState(placeholderImage);
|
||||
const fileInputRef = useRef(); // Referencja do ukrytego inputu plików
|
||||
|
||||
const handleImageChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
setImageSrc(e.target.result); // Aktualizujemy stan obrazkiem użytkownika
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDivClick = () => {
|
||||
fileInputRef.current.click(); // Programowe kliknięcie na ukrytym inpucie plików
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// Aktualizujemy dane w hooku nadrzędnym za każdym razem, gdy zmienia się imageSrc
|
||||
if (imageSrc !== placeholderImage) {
|
||||
setFormData(prevFormData => ({
|
||||
...prevFormData,
|
||||
image: imageSrc
|
||||
}));
|
||||
}
|
||||
}, [imageSrc, setFormData]);
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
onClick={handleDivClick}
|
||||
className='h-[12rem] rounded-lg border-[0.3rem] border-slate-300'
|
||||
style={{
|
||||
backgroundImage: `url(${imageSrc})`,
|
||||
backgroundSize: 'contain',
|
||||
backgroundRepeat: 'no-repeat',
|
||||
backgroundPosition: 'center',
|
||||
backgroundColor: '#FFFFFF',
|
||||
}}
|
||||
>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
id="avatar"
|
||||
name="avatar"
|
||||
accept="image/png, image/jpeg"
|
||||
onChange={handleImageChange}
|
||||
style={{ display: 'none' }} // Ukryj input plików
|
||||
/>
|
||||
</div>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImageUpload;
|
||||
@ -4,7 +4,7 @@ import { close, search, menu, IzaacLOGO} from '../assets';
|
||||
import { useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import Mininav from './Mininav';
|
||||
const NavBar = () => {
|
||||
const[toggle, setToggle] = useState(false)
|
||||
const[toggleSearch, setToggleSearch] = useState(false)
|
||||
@ -20,8 +20,8 @@ const NavBar = () => {
|
||||
const currentLinks = getLinks()
|
||||
|
||||
return (
|
||||
<nav className='w-full flex py-6 justify-between items-center navbar '>
|
||||
|
||||
<nav className='w-full flex pt-7 justify-between items-center navbar '>
|
||||
|
||||
<img src={IzaacLOGO} alt="izaac" className='md:w-[200px] w-[120px]'/>
|
||||
|
||||
<ul className='list-none sm:flex hidden justify-end items-center flex-1'>
|
||||
|
||||
127
src/components/Salary.jsx
Normal file
@ -0,0 +1,127 @@
|
||||
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'
|
||||
]
|
||||
|
||||
const Salary = ({handleChange, formData, removeFields, setFormData}) => {
|
||||
const [requireSalary, setRequireSalary] = useState(true)
|
||||
|
||||
const handleRequireSalary = () => {
|
||||
setRequireSalary(prevRequireSalary => !prevRequireSalary);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setFormData({...formData, 'requireSalary': requireSalary});
|
||||
}, [requireSalary]);
|
||||
|
||||
const [minBigger, setMinBigger] = useState(true);
|
||||
const handleCheck = () => {
|
||||
let minSalary = parseInt(formData.minSalary);
|
||||
let maxSalary = parseInt(formData.maxSalary);
|
||||
if (minSalary > maxSalary) {
|
||||
setMinBigger(true)
|
||||
}
|
||||
else {
|
||||
setMinBigger(false)
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
handleCheck();
|
||||
console.log(minBigger)
|
||||
console.log(formData.minSalary)
|
||||
console.log(formData.maxSalary)
|
||||
|
||||
}, [formData.minSalary, formData.maxSalary]);
|
||||
useEffect(() => {
|
||||
})
|
||||
useEffect( () =>{
|
||||
if (formData.requireSalary === false) {
|
||||
removeFields(['minSalary', 'maxSalary']);
|
||||
setRequireSalary(formData.requireSalary)
|
||||
}
|
||||
}, [formData.requireSalary])
|
||||
|
||||
|
||||
return (
|
||||
<div className='grid grid-cols-4 mt-4 gap-6'>
|
||||
<div className='col-span-4'>
|
||||
<input
|
||||
className='ml-6'
|
||||
type="checkbox"
|
||||
checked={!requireSalary}
|
||||
name="requireSalary"
|
||||
onChange={handleRequireSalary}
|
||||
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>
|
||||
|
||||
<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'] || '' }
|
||||
|
||||
onChange={handleChange('minSalary')}
|
||||
type='number'
|
||||
className={`h-12 border-2 rounded-xl px-5 mx-4 w-full ${!requireSalary ? '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>
|
||||
</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')}
|
||||
type='number'
|
||||
className={`h-12 border-2 rounded-xl px-5 mx-4 w-full ${!requireSalary ? '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' }
|
||||
name='employmentType'
|
||||
onChange={handleChange('employmentType')}
|
||||
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>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<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' }
|
||||
name='workFromHome'
|
||||
onChange={handleChange('workFromHome')}
|
||||
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>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Salary
|
||||
66
src/components/SelectedSkill.jsx
Normal file
@ -0,0 +1,66 @@
|
||||
import React from 'react';
|
||||
|
||||
const levels = ['Nice to have','Podstawowy', 'Średnio zaawansowany', 'Zaawansowany', 'Ekspert'];
|
||||
|
||||
function renderCircles(level, handleCircleClick) {
|
||||
let numberOfFilledCircles;
|
||||
|
||||
switch (level) {
|
||||
case 'Nice to have':
|
||||
numberOfFilledCircles = 1;
|
||||
break;
|
||||
case 'Podstawowy':
|
||||
numberOfFilledCircles = 2;
|
||||
break;
|
||||
case 'Średnio zaawansowany':
|
||||
numberOfFilledCircles = 3;
|
||||
break
|
||||
case 'Zaawansowany':
|
||||
numberOfFilledCircles = 4;
|
||||
break;
|
||||
case 'Ekspert':
|
||||
numberOfFilledCircles = 5;
|
||||
break;
|
||||
default:
|
||||
numberOfFilledCircles = 1;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{[...Array(5)].map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => handleCircleClick(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 SelectedSkill = ({ skill, level, onLevelChange, removeSkillFromList, formData }) => {
|
||||
const levelss = 'Nice to have';
|
||||
const handleCircleClick = (levelIndex) => {
|
||||
const level = levels[levelIndex];
|
||||
onLevelChange(skill, level);
|
||||
};
|
||||
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">
|
||||
×
|
||||
</button>
|
||||
<div className='h-0.5 w-full bg-dimWhite opacity-60 mt-1'></div>
|
||||
<div className='grid grid-cols-5 mt-2'>
|
||||
{renderCircles(level, handleCircleClick)}
|
||||
</div>
|
||||
<p className='font-poppins font-semibold text-slate-700 text-center text-[12px] mt-1'>{level}</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectedSkill;
|
||||
143
src/components/SkillsSelector.jsx
Normal file
@ -0,0 +1,143 @@
|
||||
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',
|
||||
// ... więcej umiejętności ...
|
||||
];
|
||||
|
||||
const SkillsSelector = ({formData, setFormData}) => {
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [suggestions, setSuggestions] = useState([]);
|
||||
const [selectedSkills, setSelectedSkills] = useState([]);
|
||||
|
||||
const handleInputChange = (e) => {
|
||||
const value = e.target.value;
|
||||
setInputValue(value);
|
||||
if (value.length >= 0) {
|
||||
setSuggestions(initialSkillsList.filter(skill =>
|
||||
skill.toLowerCase().includes(value.toLowerCase())));
|
||||
} else {
|
||||
setSuggestions([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSuggestionClick = (skill) => {
|
||||
if (!selectedSkills.includes(skill)) {
|
||||
setSelectedSkills(prevSkills => [skill, ...prevSkills]);
|
||||
};
|
||||
setSkillLevels(prevLevels => ({
|
||||
...prevLevels,
|
||||
[skill]: 'Nice to have' // lub inny domyślny poziom
|
||||
}));
|
||||
setInputValue('');
|
||||
setSuggestions([]);
|
||||
};
|
||||
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 }));
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
// Aktualizacja formData, aby zawierała skillLevels
|
||||
setFormData(prevFormData => ({
|
||||
...prevFormData,
|
||||
skillLevels: skillLevels
|
||||
}));
|
||||
}, [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, ]);
|
||||
|
||||
return (
|
||||
<div className='relative mt-3 grid grid-cols-5'>
|
||||
<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"
|
||||
/>
|
||||
</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 */}
|
||||
{selectedSkills.map(skill => (
|
||||
|
||||
<SelectedSkill
|
||||
key={skill}
|
||||
skill={skill}
|
||||
level={skillLevels[skill] || 'Nice to have'} // Domyślny poziom, jeśli nie ustawiony
|
||||
onLevelChange={handleLevelChange}
|
||||
removeSkillFromList={removeSkillFromList}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SkillsSelector;
|
||||
120
src/components/StepFourJoblisting.jsx
Normal file
@ -0,0 +1,120 @@
|
||||
import React from "react";
|
||||
import styles from "../style";
|
||||
import TextDivider from "./TextDivider";
|
||||
const StepFourJoblisting = ({ handleChange, formData, nextStep, prevStep }) => {
|
||||
return (
|
||||
<div className={`${styles.flexCenter}`}>
|
||||
<div className="grid grid-cols-5">
|
||||
<div className="col-span-3 col-start-2 mt-12">
|
||||
<TextDivider text={`Podaj dane ogłoszeniodawcy`} />
|
||||
<div className="grid grid-cols-3">
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
Imię
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="first_name"
|
||||
value={formData["first_name"] || ""}
|
||||
required
|
||||
id="first_name"
|
||||
onChange={handleChange("first_name")}
|
||||
placeholder="Twoje imię..."
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
Nazwisko
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
name="lastname"
|
||||
value={formData["lastname"] || ""}
|
||||
required
|
||||
id="lastname"
|
||||
onChange={handleChange("lastname")}
|
||||
placeholder="Twoje nazwisko..."
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
Adres e-mail
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span>
|
||||
</div>
|
||||
<input
|
||||
type="email"
|
||||
name="email"
|
||||
value={formData["email"] || ""}
|
||||
required
|
||||
id="email"
|
||||
onChange={handleChange("email")}
|
||||
placeholder="Adres mailowy..."
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph}`}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-span-2 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
Nazwa firmy
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-2">
|
||||
<input
|
||||
type="text"
|
||||
name="company_name"
|
||||
value={formData["company_name"] || ""}
|
||||
required
|
||||
onChange={handleChange("company_name")}
|
||||
id="company_name"
|
||||
placeholder="Wpisz nazwę firmy..."
|
||||
className={`border-b-2 px-4 mr-12 my-2 ${styles.paragraph} col-span-2 `}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-1 mt-4 mx-2">
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>
|
||||
Nazwa firmy
|
||||
<span className={`${styles.paragraph} text-red-700`}>*</span>
|
||||
</div>
|
||||
<input
|
||||
type="number"
|
||||
name="vat_number"
|
||||
value={formData["vat_number"] || ""}
|
||||
required
|
||||
maxLength="10"
|
||||
onChange={handleChange("vat_number")}
|
||||
id="vat_number"
|
||||
placeholder="Wpisz nip..."
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph} `}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${styles.flexCenter} gap-16 mt-16`}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={prevStep}
|
||||
className="h-12 w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] scale-100 text-white hover:scale-125 duration-300"
|
||||
>
|
||||
<span className="text-[18px]">←</span> Przejdź do
|
||||
poprzedniego kroku
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={nextStep}
|
||||
className="h-12 w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] scale-100 text-white hover:scale-125 duration-300"
|
||||
>
|
||||
Przejdź do następnego kroku
|
||||
<span className="text-[18px]">→</span>{" "}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default StepFourJoblisting;
|
||||
68
src/components/StepOneJoblisting.jsx
Normal file
@ -0,0 +1,68 @@
|
||||
import React from 'react'
|
||||
import styles from '../style'
|
||||
const StepOneJoblisting = ({ nextStep, handleChange, formData }) => {
|
||||
// Funkcja do obsługi kliknięcia na div i aktualizacji stanu
|
||||
const handleDivClick = (value) => () => {
|
||||
handleChange('postingOption')({ target: { value: value } });
|
||||
};
|
||||
|
||||
const activeStyle = "h-[500px] w-64 border-4 rounded-xl border-stone-200 bg-gray-200 div-transition scale-125";
|
||||
const inactiveStyle = "h-[500px] w-64 rounded-xl bg-gray-200 border-gray-200 div-transition cursor-pointer hover:bg-gray-300"; // Dodaj tę samą klasę przejścia tutaj
|
||||
|
||||
return (
|
||||
<form>
|
||||
<div className={` grid grid-cols-1 ${styles.paddingX} pb-8 pt-8 gap-1 h-full`}>
|
||||
<h1 className={`${styles.heading1} text-center`}>Zacznij dodawać ogłoszenie z izaac.pl</h1>
|
||||
<p className={`${styles.paragraph} text-center`}>Wybierz pakiet najlepiej odpowiadający Twoim potrzebom</p>
|
||||
|
||||
</div>
|
||||
<div className={` ${styles.flexStart} ${styles.paddingX} gap-16 mt-20`}>
|
||||
{/* Przykładowe divy jako przyciski wyboru */}
|
||||
|
||||
<div
|
||||
className={formData.postingOption === 'Opcja 1' ? activeStyle : inactiveStyle}
|
||||
onClick={handleDivClick('Opcja 1')}
|
||||
|
||||
>
|
||||
<p className={`${styles.paragraph} text-center mt-4`}>Starter</p>
|
||||
|
||||
</div>
|
||||
<div className='h-[400px] w-0.5 bg-gray-300'>
|
||||
|
||||
</div>
|
||||
<div
|
||||
className={formData.postingOption === 'Opcja 2' ? activeStyle : inactiveStyle}
|
||||
onClick={handleDivClick('Opcja 2')}
|
||||
>
|
||||
<p className={`${styles.paragraph} text-center mt-4`}>Standard</p>
|
||||
<div className='mt-6'>
|
||||
|
||||
<p className={`${styles.paragraph} text-center`}>najczęsciej wybierany</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='h-[400px] w-0.5 bg-gray-300'>
|
||||
|
||||
</div>
|
||||
<div
|
||||
className={formData.postingOption === 'Opcja 3' ? activeStyle : inactiveStyle}
|
||||
onClick={handleDivClick('Opcja 3')}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
<p className={`${styles.paragraph} text-center mt-4`}>Premium</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className='grid pt-32 items-center justify-center'>
|
||||
<button
|
||||
type="button"
|
||||
onClick={nextStep}
|
||||
className='h-12 w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] text-white hover:scale-125 duration-300'>Przejdź do następnego kroku <span className='text-[18px]'>→</span> </button>
|
||||
<div className='h-12 w-full'></div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default StepOneJoblisting
|
||||
88
src/components/StepThreeJoblisting.jsx
Normal file
@ -0,0 +1,88 @@
|
||||
import React from 'react';
|
||||
import DOMPurify from 'dompurify';
|
||||
import styles from '../style';
|
||||
import TextDivider from './TextDivider';
|
||||
|
||||
const StepThreeJoblisting = ({ formData, prevStep }) => {
|
||||
// Funkcja do przetwarzania HTML i dodawania klas
|
||||
const processHTML = (htmlString) => {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(htmlString, 'text/html');
|
||||
|
||||
// Dodaj klasy do tagów h1, h2, ..., h6
|
||||
doc.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach(tag => {
|
||||
tag.classList.add('no-tailwindcss-base');
|
||||
});
|
||||
|
||||
// Dodaj klasy do tagów ol, ul
|
||||
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;
|
||||
};
|
||||
|
||||
// Sanituj i przetwórz dane
|
||||
const cleanAndProcessData = (userInput) => {
|
||||
const sanitizedHTML = DOMPurify.sanitize(userInput);
|
||||
return processHTML(sanitizedHTML);
|
||||
};
|
||||
|
||||
// Przykład renderowania przetworzonej treści jako komponentów React
|
||||
const renderContent = (htmlString) => {
|
||||
return <div dangerouslySetInnerHTML={{ __html: cleanAndProcessData(htmlString) }} />;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='text-center'>
|
||||
<p className='mt-8 text-[24px] font-poppins font-bold '>
|
||||
Wybrałeś opcję {formData.postingOption}
|
||||
</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.jobtitle)}</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>
|
||||
</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'>
|
||||
{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'>
|
||||
<span className='text-[18px]'>←</span> Przejdź do poprzedniego kroku
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default StepThreeJoblisting;
|
||||
178
src/components/StepTwoJoblisting.jsx
Normal file
@ -0,0 +1,178 @@
|
||||
import React, { useState } from 'react'
|
||||
import styles from '../style'
|
||||
import { CKEditor } from '@ckeditor/ckeditor5-react';
|
||||
import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
|
||||
import ImageUpload from './ImageUpload';
|
||||
import SkillsSelector from './SkillsSelector';
|
||||
import Salary from './Salary';
|
||||
|
||||
const StepTwoJoblisting = ({ nextStep, prevStep, handleChange, formData, setFormData, removeFields }) => {
|
||||
const [editorData, setEditorData] = useState('');
|
||||
const handleEditorChange = (event, editor) => {
|
||||
const data = editor.getData();
|
||||
setEditorData(data);
|
||||
}
|
||||
|
||||
const handleNextStep = () => {
|
||||
if (validateForm()) {
|
||||
console.log(errors)
|
||||
nextStep(); // Przejście do następnego kroku
|
||||
} else {
|
||||
alert('Proszę wypełnić wszystkie wymagane pola.'); // Wyświetl komunikat o błędzie
|
||||
}
|
||||
};
|
||||
const errorStyleInput = 'border-red-600'
|
||||
const [errors, setErrors] = useState({});
|
||||
const validateForm = () => {
|
||||
let newErrors = {};
|
||||
|
||||
// Sprawdź każde wymagane pole
|
||||
if (!formData.jobtitle) {
|
||||
newErrors.jobtitle = 'To pole jest wymagane';
|
||||
}
|
||||
if (!formData.company_name) {
|
||||
newErrors.company_name = 'To pole jest wymagane';
|
||||
}
|
||||
if (!formData.localization) {
|
||||
newErrors.localization = 'To pole jest wymagane';
|
||||
}
|
||||
if (!formData.content) {
|
||||
newErrors.content = 'To pole jest wymagane';
|
||||
}
|
||||
console.log(formData.requireSalary)
|
||||
if (formData.requireSalary === true)
|
||||
{
|
||||
console.log(formData.requireSalary)
|
||||
if (!formData.minSalary) {
|
||||
newErrors.minSalary = 'To pole jest wymagane';
|
||||
}
|
||||
if (!formData.maxSalary) {
|
||||
newErrors.maxSalary = 'To pole jest wymagane';
|
||||
}
|
||||
}
|
||||
|
||||
if (!formData.employmentType) {
|
||||
newErrors.employmentType = 'To pole jest wymagane';
|
||||
}
|
||||
if (!formData.employmentType) {
|
||||
newErrors.employmentType = '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';
|
||||
}
|
||||
setErrors(newErrors);
|
||||
|
||||
// Zwróć true, jeśli nie ma błędów
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className='grid grid-cols-3'>
|
||||
<div className='sm:px-20 px-16 sm:pt-3 pt-6 pb-6 xl:col-span-3 col-span-3'>
|
||||
<span className='justify-center'>
|
||||
<p className={`${styles.heading1} text-center`}>Dodajesz ogłoszenie z izaac.pl</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='no-tailwindcss-base'>
|
||||
<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={`${styles.paragraph} px-4 py-2`}>Nazwa ogłoszenia<span className={`${styles.paragraph} text-red-700`}>*</span></div>
|
||||
<input type="text"
|
||||
name="jobtitle"
|
||||
value={formData['jobtitle'] || ''}
|
||||
required
|
||||
id="jobtitle"
|
||||
onChange={handleChange('jobtitle')}
|
||||
placeholder='Wpisz nazwę stanowiska...'
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph} col-span-2 ${errors.jobtitle ? errorStyleInput : ''}`}/>
|
||||
|
||||
<div className={`${styles.paragraph} px-4 py-2 `}>Strona internetowa</div>
|
||||
<input type="text"
|
||||
name="webpage"
|
||||
value={formData['webpage'] || ''}
|
||||
onChange={handleChange('webpage')}
|
||||
id="webpage"
|
||||
placeholder='Wpisz adres strony internetowej...'
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph} col-span-2`}/>
|
||||
<div className={`${styles.paragraph} px-4 py-2 `}>Adres siedziby<span className={`${styles.paragraph} text-red-700`}>*</span></div>
|
||||
<input type="text"
|
||||
name="localization"
|
||||
required
|
||||
value={formData['localization'] || ''}
|
||||
onChange={handleChange('localization')}
|
||||
id="localization"
|
||||
placeholder='Wpisz adres siedziby...'
|
||||
className={`border-b-2 px-4 my-2 ${styles.paragraph} col-span-2`}/>
|
||||
<div className={`${styles.paragraph} px-4 py-2 `}>Logo firmy<span className={`${styles.paragraph} text-red-700`}>*</span></div>
|
||||
<div className='mt-6 col-span-2'>
|
||||
<ImageUpload
|
||||
setFormData={setFormData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full border-b-2'></div>
|
||||
<div className={`${styles.paragraph} px-4 py-2`}>Treść ogłoszenia <span className={`${styles.paragraph} text-red-700`}>*</span></div>
|
||||
<CKEditor
|
||||
editor={ClassicEditor}
|
||||
data={formData['content'] || ''}
|
||||
required
|
||||
onChange={(event, editor) => {
|
||||
const data = editor.getData();
|
||||
handleChange('content')(data); // Wywołanie zmodyfikowanej funkcji handleChange
|
||||
}}
|
||||
onReady={ editor => {
|
||||
// You can store the "editor" and use when it is needed.
|
||||
// console.log( 'Editor is ready to use!', editor );
|
||||
} }
|
||||
onBlur={ ( event, editor ) => {
|
||||
// console.log( 'Blur.', editor );
|
||||
} }
|
||||
onFocus={ ( event, editor ) => {
|
||||
// console.log( 'Focus.', editor );
|
||||
} }
|
||||
/>
|
||||
<div className='grid grid-cols-3 items-center mt-4'>
|
||||
<span className={`${styles.paragraph} col-span-3 px-4`}>Dodaj wybrane umiejętności <span className={`${styles.paragraph} text-red-700`}>*</span></span>
|
||||
<div className='col-span-3'>
|
||||
<SkillsSelector
|
||||
formData={formData}
|
||||
setFormData={setFormData}
|
||||
/>
|
||||
</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></div>
|
||||
<div className=''>
|
||||
<Salary
|
||||
removeFields={removeFields}
|
||||
setFormData={setFormData}
|
||||
handleChange={handleChange}
|
||||
formData={formData}/>
|
||||
</div>
|
||||
|
||||
<div className={`${styles.flexCenter} p-20 gap-20`}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={prevStep}
|
||||
className='h-12 w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] scale-100 text-white hover:scale-125 duration-300'><span className='text-[18px]'>←</span> Przejdź do poprzedniego kroku</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleNextStep}
|
||||
className='h-12 w-72 rounded-xl bg-gray-700 font-poppins font-semibold text-[14px] scale-100 text-white hover:scale-125 duration-300'>Przejdź do następnego kroku <span className='text-[18px]'>→</span> </button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default StepTwoJoblisting
|
||||
16
src/components/TextDivider.jsx
Normal file
@ -0,0 +1,16 @@
|
||||
import React from 'react'
|
||||
|
||||
const TextDivider = ({text }) => {
|
||||
return (
|
||||
<div className='flex items-center justify-center mt-4 mb-6'>
|
||||
<div className='h-0.5 bg-black w-[14rem] opacity-30'></div>
|
||||
<p className='text-[24px] mx-8 font-semibold'>
|
||||
{text}
|
||||
</p>
|
||||
<div className='h-0.5 bg-black w-[14rem] opacity-30'></div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
export default TextDivider
|
||||
@ -209,7 +209,7 @@ export const linki = [
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"name": "work/jobpostings",
|
||||
"name": "work/postings",
|
||||
"title": "Ogłoszenia",
|
||||
},
|
||||
{
|
||||
|
||||
BIN
src/icon/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/icon/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
src/icon/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/icon/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 502 B |
BIN
src/icon/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src/icon/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
1
src/icon/site.webmanifest
Normal file
@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
@ -25,6 +25,7 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
|
||||
:root {
|
||||
--black-gradient: linear-gradient(
|
||||
144.39deg,
|
||||
@ -38,20 +39,35 @@
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
.editor-container {
|
||||
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: 4rem;
|
||||
padding-right: 4rem;
|
||||
padding-left: 12rem;
|
||||
padding-right: 12rem;
|
||||
|
||||
;
|
||||
}
|
||||
|
||||
.div-transition {
|
||||
transition: all 0.3s ease;
|
||||
scale: 100%;
|
||||
}
|
||||
|
||||
.ck-editor__editable {
|
||||
min-height: 30rem;
|
||||
min-height: 12rem;
|
||||
max-height: 12rem;
|
||||
|
||||
}
|
||||
.ck.ck-editor {
|
||||
min-height: 140rem;
|
||||
|
||||
.job-listing {
|
||||
-webkit-mask-image: linear-gradient(to top, black 0%, transparent 100%);
|
||||
mask-image: linear-gradient(to top, black 0%, transparent 100%);
|
||||
}
|
||||
.job-listing2 {
|
||||
-webkit-mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
|
||||
mask-image: linear-gradient(to bottom, black 0%, transparent 100%);
|
||||
}
|
||||
|
||||
.lista {
|
||||
list-style: inside;
|
||||
}
|
||||
|
||||
.sidebar-show {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
const styles = {
|
||||
boxWidth: "xl:max-w-[1280px] w-full",
|
||||
boxWidth2: "w-full",
|
||||
heading1: "font-poppins font-semibold xs:text-[40px] text-[40px] text-black xs:leading-[76.8px] leading-[66.8px] w-full",
|
||||
|
||||
heading2: "font-poppins font-semibold xs:text-[48px] text-[40px] text-black xs:leading-[76.8px] leading-[66.8px] w-full",
|
||||
paragraph: "font-poppins font-normal text-black text-[18px] leading-[30.8px]",
|
||||
|
||||