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

Feat/practice interview questions #163

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

<!-- CONTRIBUTING -->

<p align="right">(<a href="#readme-top">back to top</a>)</p>
Expand Down
128 changes: 128 additions & 0 deletions cypress/e2e/jobApplicationInterviewQuestionSpec.cy.js
Original file line number Diff line number Diff line change
@@ -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: "[email protected]",
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("[email protected]");
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$/);
});
});
84 changes: 84 additions & 0 deletions cypress/fixtures/mockInterviewQuestions.json
Original file line number Diff line number Diff line change
@@ -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": "[email protected]",
"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?"
}
]
}
}
}
]
}
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -46,6 +47,7 @@ function App() {
<Route path="/contacts/new" element={<NewContact userData={userData}/>} />
<Route path="/job_applications" element={<ApplicationsGrid/>}/>
<Route path="/job_applications/:jobAppId" element={<JobApplication/>}/>
<Route path="/job_applications/:jobAppId/interviewQuestions" element={<JobApplicationInterviewQuestions/>}/>
<Route
path="/userInformation"
element={<UserInformation userData={userData} />}
Expand Down
Binary file added src/assets/PracticeInterview.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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<number, string>);

return (
<div className="min-h-screen p-4 sm:p-8 pt-8 sm:pt-36">
<main>
<h1 className="text-cyan-600 text-3xl sm:text-4xl lg:text-5xl font-semibold mb-4">
{positionTitle}
</h1>
<Link className="font-bold text-cyan-800 hover:text-cyan-700 p-0 hover:underline" to={`/companies/${companyId}/contacts`}>
<h2 className="text-[3.5vh] font-bold text-cyan-500 hover:text-cyan-700 p-0 hover:underline">
{companyName}
</h2>
</Link>
< Link to={`/job_applications/${jobAppId}`}>
<h3 className="underline underline-offset-[7px] text-cyan-600 text-[2.3vh]">
Back to job application details
</h3>
</Link>
<h4 className="mt-10 text-[20px] font-bold text-cyan-600 tracking-wide">
Technical Interview Questions
</h4>
{Object.entries(questionObject).map(([id, text]) => (
<div key={id} data-testid="interview-questions-list" className="ml-5 text-gray-600 w-1/2 text-[17px]">
<br>
</br>
{id}. {text}
</div>
))}
</main>
</div>
);
}

export default JobApplicationInterviewQuestions
8 changes: 8 additions & 0 deletions src/components/pages/showJobApplication.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,14 @@ function JobApplication() {

<section className="mt-8 lg:mt-0">
<div className="mb-8">
<div className="flex justify-end" data-testid="interview-questions"
>
<Link to={`/job_applications/${jobAppId}/interviewQuestions`} state={{jobAppId: jobAppId, positionTitle: positionTitle, companyName: jobApp.company_name, companyId: companyId, jobDescription:jobDescription}}>
<button className="bg-cyan-600 hover:bg-cyan-500 text-white tracking-wide py-2 px-4 rounded max-w-max">
Practice Interview ✨
</button>
</Link>
</div>
<h2 className="text-cyan-600 text-2xl sm:text-3xl mb-4">
Job Description
</h2>
Expand Down