Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added final changes #32

Merged
merged 1 commit into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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