diff --git a/README.md b/README.md index 52bf972..3633248 100644 --- a/README.md +++ b/README.md @@ -290,6 +290,9 @@ Choose a company that exists under your profile. - Edit an Application Date or Status: Job application dates and statuses can also be edited directly on the job application page. Dates can be changed by clicking on the "Applied On" date to pull up an interactive calendar, and clicking on a new date. Statuses can be changed by clicking on the current status and selecting a new status from the dropdown. ![Edit Job Application Date Page](./src/assets/editJobAppDateOrStatus.png) +- Practice Interview: Ten technical interview questions are generated based on the job description you share. These questions are to help prepare for a potential interview. +![Practice Job Interview](./src/assets/PracticeInterview.gif) +

(back to top)

diff --git a/cypress/e2e/jobApplicationInterviewQuestionSpec.cy.js b/cypress/e2e/jobApplicationInterviewQuestionSpec.cy.js new file mode 100644 index 0000000..9008e2b --- /dev/null +++ b/cypress/e2e/jobApplicationInterviewQuestionSpec.cy.js @@ -0,0 +1,128 @@ +describe("View specific job app page with all fields filled in", () => { + beforeEach(() => { + cy.intercept("POST", "http://localhost:3001/api/v1/sessions", { + statusCode: 200, + body: { + token: "fake-token", + user: { + data: { + id: 1, + type: "user", + attributes: { + name: "Test User", + email: "testuser@example.com", + companies: [], + }, + }, + }, + }, + }).as("mockSession"); + + cy.intercept( + "GET", + "http://localhost:3001/api/v1/users/1/job_applications", + (req) => { + req.on("response", (res) => {}); + req.reply({ + statusCode: 200, + fixture: "mockJobApps", + headers: { + "Content-Type": "application/json", + }, + }); + } + ).as("getJobApplications"); + + cy.intercept( + "GET", + "http://localhost:3001/api/v1/users/1/job_applications/3", + (req) => { + req.on("response", (res) => {}); + req.reply({ + statusCode: 200, + fixture: "mockSingleJobApp", + headers: { + "Content-Type": "application/json", + }, + }); + } + ).as("showSingleJobApp"); + + cy.intercept( + "GET", + "http://localhost:3001/api/v1/users/1/job_applications/3/interviewQuestions", + (req) => { + req.on("response", (res) => {}); + req.reply({ + statusCode: 200, + fixture: "mockInterviewQuestions", + headers: { + "Content-Type": "application/json", + }, + }); + } + ).as("showJobInterviewQuestions"); + + cy.intercept( + "GET", + "http://localhost:3001/api/v1/users/1/companies/3/contacts", + (req) => { + req.headers["Authorization"] = "Bearer fake-token"; + req.reply({ + statusCode: 200, + fixture: "mockCompanyDetails", + headers: { + "Content-Type": "application/json", + }, + }); + } + ).as("getCompanyDetails"); + + cy.visit("http://localhost:3000/"); + cy.get("#email").type("danny_de@email.com"); + cy.get("#password").type("jerseyMikesRox7"); + cy.get('[data-testid="login-button"]').click(); + cy.get('[data-testid="applications-iconD"]').click(); + cy.get('[data-testid="applications-iconD"]').click(); + cy.get("tbody > tr").contains("Creative Solutions Inc.").click(); + cy.get('[data-testid="interview-questions"] button').click(); + }); + + it("displays the position title and company name", () => { + cy.get("h1.text-cyan-600") + .should("have.text", "Backend Developer") + .next() + .should("have.text", "Creative Solutions Inc.") + .next() + .should("have.text", "Back to job application details") + .next() + .should("have.text","Technical Interview Questions"); + }); + + it("navigates to the company details page", () => { + cy.get("h2").contains("Creative Solutions Inc.").click(); + cy.wait("@getCompanyDetails"); + + cy.location("pathname").should("match", /\/companies\/3\/contacts$/); + }); + + it("has interview questions", () => { + cy.url().should('include', '/interviewQuestions'); + + cy.get('[data-testid="interview-questions-list"]') + .should('exist') + .and('have.length', 10); + }); + + it("displays interview questions", () => { + cy.get('[data-testid="interview-questions-list"]') + .should("have.text", "1. Can you explain the difference between state and props in React?2. Given a React component that fetches data from an API, how would you manage loading, success, and error states?3. How would you handle form submission and validation in a React application?4. How does JavaScript handle asynchronous operations? Can you explain async/await and provide an example?5. What are some techniques to improve performance in a React application?6. How would you define a RESTful API in Ruby on Rails?7. Given a User model with has_many :bookings, how would you retrieve all bookings for a user in Rails?8. How would you implement authentication in a Rails API using Devise or JWT?9. Given an array of integers, write a function that returns the two numbers that sum to a given target.10. Imagine you're working on a React/Rails app and an API request fails with a 500 error. How would you go about debugging the issue?") + }); + + it("navigates back to the job application details page", () => { + cy.get("h3").contains("Back to job application details").click(); + cy.wait("@showSingleJobApp"); + + cy.location("pathname").should("match", /\/job_applications\/3$/); + }); +}); \ No newline at end of file diff --git a/cypress/fixtures/mockInterviewQuestions.json b/cypress/fixtures/mockInterviewQuestions.json new file mode 100644 index 0000000..4190212 --- /dev/null +++ b/cypress/fixtures/mockInterviewQuestions.json @@ -0,0 +1,84 @@ +{ + "data": [ + { + "id": "3", + "type": "job_application", + "attributes": { + "position_title": "Backend Developer", + "date_applied": "2024-08-20", + "status": 2, + "notes": "Had a technical interview, awaiting decision.", + "job_description": "Developing RESTful APIs and optimizing server performance.", + "application_url": "https://creativesolutions.com/careers/backend-developer", + "company_id": 3, + "company_name": "Creative Solutions Inc.", + "contacts": [ + { + "id": 3, + "first_name": "Michael", + "last_name": "Johnson", + "email": "michael.johnson@example.com", + "phone_number": "123-555-9012", + "notes": "Hiring manager at Creative Solutions Inc." + } + ] + }, + "relationships": { + "questions": { + "data": [ + { + "id": 1, + "question_number": 1, + "question": "Can you explain the difference between state and props in React?" + }, + { + "id": 2, + "question_number": 2, + "question": "Given a React component that fetches data from an API, how would you manage loading, success, and error states?" + }, + { + "id": 3, + "question_number": 3, + "question": "How would you handle form submission and validation in a React application?" + }, + { + "id": 4, + "question_number": 4, + "question": "How does JavaScript handle asynchronous operations? Can you explain async/await and provide an example?" + }, + { + "id": 5, + "question_number": 5, + "question": "Can you explain the difference between state and props in React?What are some techniques to improve performance in a React application?" + }, + { + "id": 6, + "question_number": 6, + "question": "How would you define a RESTful API in Ruby on Rails?" + }, + { + "id": 7, + "question_number": 7, + "question": "Given a User model with has_many :bookings, how would you retrieve all bookings for a user in Rails?" + }, + { + "id": 8, + "question_number": 8, + "question": "How would you implement authentication in a Rails API using Devise or JWT?" + }, + { + "id": 9, + "question_number": 9, + "question": "Given an array of integers, write a function that returns the two numbers that sum to a given target." + }, + { + "id": 10, + "question_number": 10, + "question": " Imagine you're working on a React/Rails app and an API request fails with a 500 error. How would you go about debugging the issue?" + } + ] + } + } + } + ] +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 738f2cb..df7dbdd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,6 +18,7 @@ import ShowContact from './components/contacts/ShowContact'; import JobApplication from './components/pages/showJobApplication'; import DashBoard from "./components/dashboard/dashboard"; import { ErrorProvider } from "./context/ErrorContext"; +import JobApplicationInterviewQuestions from "./components/JobApplications/JobApplicationInterviewQuestions" function App() { @@ -46,6 +47,7 @@ function App() { } /> }/> }/> + }/> } diff --git a/src/assets/PracticeInterview.gif b/src/assets/PracticeInterview.gif new file mode 100644 index 0000000..828f96a Binary files /dev/null and b/src/assets/PracticeInterview.gif differ diff --git a/src/components/JobApplications/JobApplicationInterviewQuestions.tsx b/src/components/JobApplications/JobApplicationInterviewQuestions.tsx new file mode 100644 index 0000000..5d2546a --- /dev/null +++ b/src/components/JobApplications/JobApplicationInterviewQuestions.tsx @@ -0,0 +1,93 @@ +import {useUserLoggedContext} from "../../context/UserLoggedContext"; +import { Link, useLocation } from "react-router-dom" +import { useEffect, useState } from "react" + +const JobApplicationInterviewQuestions: React.FC = () => { +const apiURL = process.env.REACT_APP_BACKEND_API_URL +const backendURL = `${apiURL}api/v1/` +const { token } = useUserLoggedContext(); +const [chatgptQuestions, setChatgptQuestions] = useState(null); +const location = useLocation() +const positionTitle = location.state.positionTitle +const companyId = location.state.companyId +const companyName = location.state.companyName +const jobAppId = location.state.jobAppId +const jobDescription = location.state.jobDescription + + useEffect(() => { + const fetchData = async (jobDescription: string, token: string | null) => { + try { + const response = await fetch(`${backendURL}interview_questions`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}`, + }, + body: JSON.stringify({description: jobDescription}), + }); + const result = await response.json(); + setChatgptQuestions(result.data); + console.log("chat gpt questions", chatgptQuestions) + } catch (error) { + console.error('Error:', error); + } + }; + + fetchData(jobDescription, token); + }, []); + +interface DummyQuestions { + id: number; + text: string; +} + +const questions :DummyQuestions[] = [ + { id: 1, text: "Can you explain the difference between state and props in React?" }, + { id: 2, text: "Given a React component that fetches data from an API, how would you manage loading, success, and error states?" }, + { id: 3, text: "How would you handle form submission and validation in a React application?" }, + { id: 4, text: "How does JavaScript handle asynchronous operations? Can you explain async/await and provide an example?" }, + { id: 5, text: "What are some techniques to improve performance in a React application?" }, + { id: 6, text: "How would you define a RESTful API in Ruby on Rails?" }, + { id: 7, text: "Given a User model with has_many :bookings, how would you retrieve all bookings for a user in Rails?" }, + { id: 8, text: "How would you implement authentication in a Rails API using Devise or JWT?" }, + { id: 9, text: "Given an array of integers, write a function that returns the two numbers that sum to a given target." }, + { id: 10, text: "Imagine you're working on a React/Rails app and an API request fails with a 500 error. How would you go about debugging the issue?" } +] + +const questionObject = questions.reduce((acc, question) => { + acc[question.id] = question.text + return acc +}, {} as Record); + +return ( +
+
+

+ {positionTitle} +

+ +

+ {companyName} +

+ + < Link to={`/job_applications/${jobAppId}`}> +

+ Back to job application details +

+ +

+ Technical Interview Questions +

+ {Object.entries(questionObject).map(([id, text]) => ( +
+
+
+ {id}. {text} +
+ ))} +
+
+ ); +} + +export default JobApplicationInterviewQuestions \ No newline at end of file diff --git a/src/components/pages/showJobApplication.tsx b/src/components/pages/showJobApplication.tsx index b46663a..9a17112 100644 --- a/src/components/pages/showJobApplication.tsx +++ b/src/components/pages/showJobApplication.tsx @@ -254,6 +254,14 @@ function JobApplication() {
+
+ + + +

Job Description