Skip to content

Commit

Permalink
Merge pull request #32 from unibuc-cs/finalchanges
Browse files Browse the repository at this point in the history
 Added final changes
  • Loading branch information
letitiatel authored Jan 30, 2025
2 parents 0bb8640 + 454a7e0 commit d57c47d
Show file tree
Hide file tree
Showing 30 changed files with 342 additions and 319 deletions.
17 changes: 7 additions & 10 deletions frontend/jobsnap/src/components/ConfirmDeleteModal.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
import React, { useState } from 'react';

const ConfirmDeleteModal = ({ isOpen, onClose, onConfirm, cvId }) => {
if (!isOpen) return null; // Hide the modal if it's not open
if (!isOpen) return null;

const handleConfirm = () => {
console.log("Confirm deleting CV with ID:", cvId); // Log the CV ID being deleted
onConfirm(cvId); // Pass the CV ID to confirm the deletion
console.log("Confirm deleting CV with ID:", cvId);
onConfirm(cvId);
};

return (
<div className="modal-overlay">
<div className="modal-content">
<h3>Are you sure you want to delete this?</h3>
<div className="modal-actions">
<button onClick={handleConfirm} className="bg-red-500 text-white py-2 px-4 rounded-lg mr-2">
Yes
</button>
<button onClick={onClose} className="bg-gray-500 text-white py-2 px-4 rounded-lg">
Cancel
</button>
<div className="modal">
<h2>Are you sure you want to delete this CV?</h2>
<button onClick={handleConfirm}>Yes</button>
<button onClick={onClose}>No</button>
</div>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/jobsnap/src/components/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ export default function Header() {
const { user, logout } = useAuth(); // Accesăm datele utilizatorului și funcția de logout din context
const navigate = useNavigate();
const handleLogout = () => {
logout(); // Call the logout function
navigate('/home'); // Redirect to the home page after logging out
logout();
navigate('/home');
};
return (
<header className="absolute inset-x-0 top-0 z-50">
Expand Down
6 changes: 3 additions & 3 deletions frontend/jobsnap/src/components/Hero_Section.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client'
import React, { useContext } from 'react';
import { Link } from 'react-router-dom'; // Importăm Link pentru navigare
import { AuthContext } from '../context/AuthContext'; // Importă contextul care conține informațiile despre utilizator
import { Link } from 'react-router-dom';
import { AuthContext } from '../context/AuthContext';

export default function HeroSection() {
const { user } = useContext(AuthContext); // Obținem utilizatorul din context
Expand Down Expand Up @@ -32,7 +32,7 @@ export default function HeroSection() {
{/* Afișează butonul doar dacă rolul utilizatorului este "student" */}
{user && user.role === 'student' && (
<Link
to="/start" // Navighează către pagina "/start"
to="/start"
className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
>
Get started
Expand Down
2 changes: 1 addition & 1 deletion frontend/jobsnap/src/context/AuthContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const AuthProvider = ({ children }) => {
}

const data = await response.json();
console.log('Server response:', data); // Verifică ce răspuns primești
console.log('Server response:', data);
setUser(data); // Salvează utilizatorul în starea contextului
localStorage.setItem("user", JSON.stringify(data)); // Salvează utilizatorul în localStorage
} catch (error) {
Expand Down
4 changes: 2 additions & 2 deletions frontend/jobsnap/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { AuthProvider } from './context/AuthContext'; // Importă AuthProvider
import { AuthProvider } from './context/AuthContext';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<AuthProvider> {/* Adaugă AuthProvider pentru a împacheta aplicația */}
<AuthProvider>
<App />
</AuthProvider>
</React.StrictMode>
Expand Down
73 changes: 26 additions & 47 deletions frontend/jobsnap/src/pages/CVBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ import html2pdf from 'html2pdf.js';
import axios from 'axios';
import { useAuth } from '../context/AuthContext';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css'; // Importă stilurile pentru toast
import 'react-toastify/dist/ReactToastify.css';


export default function CVBuilder() {
const { cvType } = useParams(); // Preluăm tipul de CV din ruta curentă
const { user } = useAuth(); // Obținem user-ul din contextul de autentificare
const navigate = useNavigate(); // Folosim useNavigate pentru redirecționare după salvare
const { cvType } = useParams();
const { user } = useAuth();
const navigate = useNavigate();

const notifySuccess = () => toast.success("CV a fost adăugat cu succes!");
const notifyError = () => toast.error("A apărut o eroare la adăugarea CV-ului.");
// console.log("CV Type:", cvType); // Verifică în consola browser-ului dacă preiei corect tipul de CV
const notifySuccess = () => toast.success("The CV has been saved successfully!");
const notifyError = () => toast.error("An error occurred while saving the CV.");
// console.log("CV Type:", cvType);


const cvFields = {
Expand Down Expand Up @@ -97,7 +97,7 @@ export default function CVBuilder() {
const [formData, setFormData] = useState({});
const [image, setImage] = useState(null);

// Setăm câmpurile în funcție de tipul de CV selectat

useEffect(() => {
console.log('CV Type:', cvType);
if (cvFields[cvType]) {
Expand All @@ -110,10 +110,10 @@ export default function CVBuilder() {
console.log('Invalid cvType:', cvType);
}

// Încarcă datele salvate la profil

axios.get('http://localhost:8080/api/cv-templates')
.then(response => {
setFormData(response.data); // Salvează datele în starea componentelor
setFormData(response.data);
})
.catch(error => {
console.error('Error loading CV data:', error);
Expand All @@ -136,35 +136,6 @@ export default function CVBuilder() {
}
};

// const handleSaveCV = () => {
// const cvData = { ...formData, userId: user.id }; // Adăugăm userId
// console.log(cvData)
// // Verifică dacă există o imagine
// if (image) {
// cvData.image = image; // Adăugăm imaginea
// }
//
// fetch('http://localhost:8080/api/cv-templates', {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/json'
// },
// body: JSON.stringify(cvData),
// })
// .then(response => {
// if (!response.ok) {
// throw new Error('Error saving CV');
// }
// return response.json();
// })
// .then(data => {
// console.log('CV saved:', data);
// navigate('/profile'); // Redirecționează utilizatorul după salvare
// })
// .catch(error => {
// console.error('Error:', error);
// });
// };

const handleSaveCV = async () => {
const cvData = {
Expand Down Expand Up @@ -213,17 +184,15 @@ export default function CVBuilder() {
// Redirecționează utilizatorul după 2 secunde
setTimeout(() => {
navigate('/start'); // Redirecționează la pagina de start
}, 3000); // 2000ms = 2 secunde
}, 3000);
} catch (error) {
console.error("Error adding cv:", error);
// Afișează notificare de eroare

notifyError();
}
};




const handleDownloadPDF = () => {
const element = document.getElementById('cv-preview');
const options = {
Expand All @@ -232,14 +201,17 @@ export default function CVBuilder() {
html2canvas: { scale: 3 },
};


html2pdf().set(options).from(element).save();
};


return (
<div className="min-h-screen py-20 bg-gradient-to-r from-blue-100 to-blue-300 flex flex-col items-center justify-center">
<div
className="min-h-screen py-20 bg-gradient-to-r from-blue-100 to-blue-300 flex flex-col items-center justify-center">
<h1 className="text-4xl font-extrabold text-gray-900 mb-8 text-center">
Start Building Your {cvType ? cvType.charAt(0).toUpperCase() + cvType.slice(1) : 'CV'} CV
<ToastContainer />
<ToastContainer/>
</h1>

<div className="flex flex-col lg:flex-row gap-8 w-full max-w-7xl px-4">
Expand Down Expand Up @@ -285,8 +257,8 @@ export default function CVBuilder() {
</form>

{/* Preview Section */}
<div id="cv-preview" className="bg-white shadow-lg rounded-lg p-6 w-full lg:w-1/2">
<CVTemplate formData={formData} image={image} cvType={cvType} />
<div id="cv-preview" className="cv-preview transform scale-90">
<CVTemplate formData={formData} image={image} cvType={cvType}/>
</div>
</div>

Expand All @@ -298,6 +270,13 @@ export default function CVBuilder() {
Save CV
</button>

<button
onClick={handleDownloadPDF}
className="mt-8 bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700"
>
Download CV as PDF
</button>


</div>
);
Expand Down
133 changes: 87 additions & 46 deletions frontend/jobsnap/src/pages/CVDetailPage.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import CVTemplate from "../components/CVTemplate";

const CVDetailPage = () => {
const { cvId } = useParams(); // Get cvId from URL
const { cvId } = useParams(); // Obținem cvId din URL
const [cv, setCv] = useState(null);
const [isModalOpen, setIsModalOpen] = useState(false);

useEffect(() => {
const fetchCVDetails = async () => {
Expand All @@ -26,9 +28,12 @@ const CVDetailPage = () => {
return <div>Loading...</div>;
}

// Function to check if the field has valid content (not "N/A" or empty)
const hasValidContent = (field) => {
return field && field !== 'N/A';
const handleViewCV = () => {
setIsModalOpen(true);
};

const handleCloseCV = () => {
setIsModalOpen(false);
};

return (
Expand All @@ -44,54 +49,90 @@ const CVDetailPage = () => {
)}
</div>

{/* Conditionally render fields based on content */}
{hasValidContent(cv.email) && (
<div className="contact-info mb-8">
<h3 className="text-xl font-semibold text-blue-700 mb-4">Contact Information</h3>
<p><strong>Email:</strong> {cv.email}</p>
<p><strong>Phone:</strong> {cv.phone}</p>
</div>
)}
{/* Detalii CV */}
<div className="cv-details">
<h3 className="text-xl font-semibold text-blue-700 mb-4">Contact Information</h3>
<p><strong>Email:</strong> {cv.email}</p>
<p><strong>Phone:</strong> {cv.phone}</p>

{hasValidContent(cv.education) && (
<div className="education mb-8">
<h3 className="text-xl font-semibold text-blue-700 mb-4">Education</h3>
<p>{cv.education}</p>
</div>
)}
<h3 className="text-xl font-semibold text-blue-700 mb-4">Education</h3>
<p>{cv.education}</p>

{hasValidContent(cv.experience) && (
<div className="experience mb-8">
<h3 className="text-xl font-semibold text-blue-700 mb-4">Professional Experience</h3>
<p>{cv.experience}</p>
</div>
)}
<h3 className="text-xl font-semibold text-blue-700 mb-4">Experience</h3>
<p>{cv.experience}</p>

{hasValidContent(cv.skills) && (
<div className="skills mb-8">
<h3 className="text-xl font-semibold text-blue-700 mb-4">Skills</h3>
<p>{cv.skills}</p>
</div>
)}
<h3 className="text-xl font-semibold text-blue-700 mb-4">Skills</h3>
<p>{cv.skills}</p>

{hasValidContent(cv.technologies) && (
<div className="technologies mb-8">
<h3 className="text-xl font-semibold text-blue-700 mb-4">Technologies</h3>
<p>{cv.technologies}</p>
</div>
)}
{cv.cvType === 'it' && (
<>
<h3 className="text-xl font-semibold text-blue-700 mb-4">Technologies</h3>
<p>{cv.technologies}</p>
</>
)}

{hasValidContent(cv.certifications) && (
<div className="certifications mb-8">
<h3 className="text-xl font-semibold text-blue-700 mb-4">Certifications</h3>
<p>{cv.certifications}</p>
</div>
)}
{cv.cvType === 'business' && (
<>
<h3 className="text-xl font-semibold text-blue-700 mb-4">Business Skills</h3>
<p>{cv.skills}</p>
</>
)}

{cv.cvType === 'marketing' && (
<>
<h3 className="text-xl font-semibold text-blue-700 mb-4">Marketing Skills</h3>
<p>{cv.skills}</p>
</>
)}

{cv.cvType === 'graphicdesign' && (
<>
<h3 className="text-xl font-semibold text-blue-700 mb-4">Design Skills</h3>
<p>{cv.skills}</p>
</>
)}

{cv.cvType === 'healthcare' && (
<>
<h3 className="text-xl font-semibold text-blue-700 mb-4">Healthcare Skills</h3>
<p>{cv.skills}</p>
</>
)}

{cv.cvType === 'education' && (
<>
<h3 className="text-xl font-semibold text-blue-700 mb-4">Degree</h3>
<p>{cv.degree}</p>
</>
)}

{/* Render Projects and Certifications for any CV type */}
<h3 className="text-xl font-semibold text-blue-700 mb-4">Projects</h3>
<p>{cv.projects}</p>

<h3 className="text-xl font-semibold text-blue-700 mb-4">Certifications</h3>
<p>{cv.certifications}</p>
</div>

{/* Show CV Template in Modal */}
<button
onClick={handleViewCV}
className="mt-8 bg-blue-700 text-white py-2 px-4 rounded-lg hover:bg-blue-800"
>
View CV Template
</button>

{hasValidContent(cv.projects) && (
<div className="projects mb-8">
<h3 className="text-xl font-semibold text-blue-700 mb-4">Projects</h3>
<p>{cv.projects}</p>
{isModalOpen && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white p-6 rounded-lg w-11/12 max-w-5xl shadow-2xl relative overflow-y-auto" style={{ height: "90vh" }}>
<button
onClick={handleCloseCV}
className="absolute top-4 right-4 bg-red-500 text-white px-4 py-2 rounded-full shadow-lg hover:bg-red-600"
>
Close
</button>
<CVTemplate formData={cv} image={cv.imagePath} cvType={cv.cvType} />
</div>
</div>
)}
</div>
Expand Down
Loading

0 comments on commit d57c47d

Please sign in to comment.