Skip to main content

WriteRight

WriteRight is a web application that allows users to generate cover letters quickly and easily using the power of artificial intelligence. With WriteRight, you can create professional, personalised cover letters in just a few minutes.

Live Link : WriteRight

Overview​

WriteRight is built using Next.js and integrates with the ChatGPT API to generate high-quality cover letters. The application provides a simple, user-friendly interface that makes it easy for anyone to create a cover letter with just a few clicks.

Features​

  • Simple, intuitive user interface
  • High-quality cover letter generation using ChatGPT API
  • Simply Copy to Clipboard and Download as PDF option

Tech Stack​

  • Next.js
  • React
  • ChatGPT API(OPEN API)
  • HTML/CSS

Getting Started​

To get started with WriteRight, follow these steps:

  1. Clone the repository to your local machine.

    git clone https://github.com/ishasachan/WriteRight
  2. Install dependencies using npm install.

    npm install
  3. Create a .env.local file with your ChatGPT API key (see .env.local.example for an example).

    //.env.local
    OPENAI_API_KEY=sk-HwKPWgSX8gKxuYeQwDnFT3....................
  4. Run the development server using npm run dev.

    npm run dev

Usage​

Once the development server is running, you can access the application by visiting  http://localhost:3000 in your web browser. From there, you can create a new cover letter by entering your personal information and selecting the type of cover letter you want to create.

After generating your cover letter, you can download it in PDF or Word format by clicking the corresponding button. You can also customise the formatting of your cover letter using the options provided in the application.


Project Build Process​

In this project we have used Next.Js as our framework which is a React Framework for the Web. So First we will create a Next.Js app by running the command and choosing all options to the default value

npx create-next-app@latest writeright

Now once the installation is finished we will mode inside the folder by

cd writeright

Now we will run the application

npm run dev

This will start up the development server at http://localhost:3000

Let’s open up the project in Vs Code by stopping the development server by ctrl+c and running the command code .

Let’s Look into the project file structure

The folder structure for a Next.js application is as follows:

  • .next/: This directory is generated by Next.js and contains the compiled files.
  • node_modules/: This directory contains all the packages and dependencies installed via npm.
  • pages/: This directory contains all the pages for the application. Each file in this directory corresponds to a route in the application.
  • public/: This directory contains all the static files for the application, such as images and fonts.
  • styles/: This directory contains all the CSS styles for the application.
  • components/: This directory contains all the reusable components for the application.
  • api/: This directory contains any server-side code or endpoints used by the application.
  • README.md: This file contains information about the application and how to use it.
  • package.json: This file contains information about the project and its dependencies.
  • .env.local: This file contains any environment variables specific to the local environment, such as API keys or database credentials.

Now first let’s create the backend part of the application. This will basically be the api call using the OPEN AI api in which we will send our request and we will receive a response that will be shown as a result to the users. So first lets generate our own API key.**

To generate an API key from OpenAI, follow these steps:

  1. Go to the OpenAI website at https://platform.openai.com and click on the “Log In” button located on the top right corner of the page.**
  2. If you don't have an account, create one by clicking on the "Sign up" button at the bottom of the page.**
  3. After you have logged in, navigate to the API page by clicking on "API" on the top navigation bar.**
  4. Click on the "Create API Key" button.**
  5. Fill in the required information and select the permissions you want to grant to your API key.**
  6. Click on the "Create" button.**
  7. Your API key will be displayed on the screen. Make sure to copy it and store it in a secure location.**

To use your OpenAI API key in our app, we will use the openai npm package. First, install the package:

npm install openai

Then, in your Node.js file, import the Configuration and OpenAIApi classes from the openai package and create a new instance of OpenAIApi with your API key:

import { Configuration, OpenAIApi } from 'openai';

const configuration = new Configuration({
apiKey: 'YOUR_API_KEY_HERE',
});
const openai = new OpenAIApi(configuration);

You can then use the openai instance to make API requests, such as generating text using the createCompletion method:

const completion = await openai.createCompletion({
model: 'text-davinci-002',
prompt: 'Once upon a time',
temperature: 0.7,
maxTokens: 50,
});
console.log(completion.data.choices[0].text);

Make sure to replace YOUR_API_KEY_HERE with your actual API key.

After that we will create a function to generatePrompt and send a request based on the result we want and we can also pass the parameters like ${name}

Here is the complete code for our backend part inside the pages/api folder with the name of generate-cover.js

//   /pages/api/generate-cover.js

import { Configuration, OpenAIApi } from 'openai';

const configuration = new Configuration({
apiKey: 124134113413414121414124, // paste your own api key
});
const openai = new OpenAIApi(configuration);

export default async function (req, res) {
const { name, companyName, role, requirements, about } = req.body;
const completion = await openai.createCompletion({
model: 'text-davinci-003',
prompt: generatePrompt(name, companyName, role, requirements, about),
temperature: 0.6,
max_tokens: 3000,
});
res.status(200).json({ result: completion.data.choices[0].text });
}

function generatePrompt(name, companyName, role, requirements, about) {
return `Generate a Cover letter for ${name}$ and here is information about me ${about}$. I am applying in ${companyName} for the role of ${role} with the following requirements ${requirements}.`;
}

In this same manner we can generate any prompt just like we generate in ChatGPT application and we can pass the parameters accordingly.

Now Lets start working on the Frontend Part of the Application.

All the React projects are divided into lots of different components. These components are individually build and can be reused in different parts of our application. We will divide our application into different components.

You can access all the code and view them in this Repl.it or in the github repository https://github.com/ishasachan/WriteRight

Repl.it Version​

Repl.it is an online IDE where we can write and run our codes easily and share with the world.

https://replit.com/@Abhi6722/WriteRight

Lets discuss all the components one by one

About.js​

//  /components/About.js

import styles from './About.module.css';

export default function About() {

return (
<section className={styles.about}>
<div className={styles.container}>
<h1 className={styles.title}>About WriteRight</h1>
<p className={styles.subtitle}><br />WriteRight is a powerful web application that empowers job seekers to create professional and impressive cover letters with ease.<br/><br />
<br/>Our aim is to help job seekers who struggle with creating cover letters that stand out from the rest. We understand how challenging it can be to write a cover letter that speaks to your experience and qualifications, while also grabbing the attention of potential employers. That's why we've created WriteRight, to make the process simple, fast, and effective.

<br/><br/>Our easy-to-use interface allows users to enter their personal and job-specific information, and with the help of cutting-edge technology and artificial intelligence, WriteRight generates custom, personalized cover letters in minutes. Our intelligent algorithms ensure that the cover letter is tailored to match the user's skills, experience, and qualifications to the job description, making it highly relevant and eye-catching to potential employers.

<br/><br/>With WriteRight, you can forget about the frustration of creating a cover letter from scratch or paying exorbitant fees to have a professional writer do it for you. Our application is designed to provide you with an effective and efficient solution that will save you time, money, and effort. You can easily download your cover letter in PDF or Word format, and with our advanced formatting tools, you can customize it to suit your needs.

<br/><br/>We take pride in offering a secure, user-friendly, and reliable platform that you can trust. Our team of experts is always available to help you with any questions or issues you may have, and we are continually improving and updating our application to ensure that it meets your needs and exceeds your expectations.

<br/><br/>At WriteRight, we believe that every job seeker deserves a chance to make a great first impression, and we are dedicated to helping you achieve that with a standout cover letter.</p>
</div>
</section>
);
}

This is very simple component where we have imported styles from the About.module.css file just exported a simple page with some heading and paragraph which explains about the app.

//  /components/Navbar.js

import Link from 'next/link';
import Image from 'next/image';
import styles from './Navbar.module.css';

export default function Navbar() {
return (
<nav className={styles.navbar}>
<div className={styles.logo}>
<Link href="/">
<Image src="/icon.png" alt="Logo" width={50} height={50} />
</Link>
<h1>WriteRight</h1>
</div>
<ul className={styles.navLinks}>
<li>
<Link href="/">Home</Link>
</li>
<li>
<Link href="/about">About</Link>
</li>
<li>
<Link href="https://github.com/ishasachan/WriteRight">Github</Link>
</li>
</ul>
</nav>
);
}

This code is for the Navbar component in the WriteRight application. It is responsible for rendering the navigation bar at the top of the page. The import statements at the top of the file import the necessary components and styles for the navbar. The Navbar function returns a nav element which contains the logo and links to the different pages of the application. The logo is a clickable image that links back to the home page of the application. The links to the different pages are contained in an unordered list with each list item containing a Link component from the Next.js next/link package. The styles object is used to style the different elements of the Navbar component. The logo is styled using flexbox to align the image and the logo text. The links are styled using CSS to adjust the font size, color, and padding of the list items. Overall, the Navbar component provides a simple and intuitive navigation bar for the WriteRight application. You can see the styling for the page here Navbar.module.css.

Footer.js​

//  /components/Footer.js

import styles from './Footer.module.css';

export default function Footer() {
return (
<footer className={styles.footer}>
<div className={styles.container}>
<p>&copy; 2023 WriteRight</p>
</div>
</footer>
);
}

This is the code for the Footer component, which is responsible for rendering the footer at the bottom of the page. The import statement at the top of the file imports the necessary styles for the footer. The Footer function returns a footer element which contains the copyright information and is styled using CSS from the Footer.module.css file.

Hero.js​

The following code is a React component called Hero that is responsible for rendering the hero section of the WriteRight web application. It features a large heading, a subheading, and a call-to-action button. When the button is clicked, the page scrolls down to the section with the ID "generate-cover".

//  /components/Hero.js

import styles from './Hero.module.css';

export default function Hero() {

const handleGetStartedClick = () => {
document.getElementById('generate-cover').scrollIntoView({ behavior: 'smooth' });
};

return (
<section className={styles.hero}>
<div className={styles.container}>
<h1 className={styles.title}>WriteRight</h1>
<p className={styles.subtitle}>The smart way to write cover letters</p>
<button onClick={handleGetStartedClick} className={styles.button}>Get started</button>
</div>
</section>
);
}

The Hero component imports styles from a CSS module called Hero.module.css. The component returns a section element with a class name of hero and a div element with a class name of container. The div element contains a h1 element with a class name of title, a p element with a class name of subtitle, and a button element with a class name of button. When the button is clicked, the handleGetStartedClick function is called, which scrolls the page down to the section with the ID "generate-cover" using the scrollIntoView method.

Now let’s move on to different pages of our website.

index.js​

//  /pages/index.js

import Head from 'next/head';
import React from 'react';
import Hero from '../components/Hero';
import Cover from '../components/Cover';

export default function Home() {

return (
<div>
<Head>
<title>WriteRight</title>
<link rel="icon" href="/icon.png" />
</Head>
<Hero />
<Cover />
</div>
);
}

The index page is very simple because we have just imported different components to our page that we created earlier. We use import command followed by the name through which we will access the component in our page and it can be anything. Then We will just export the default home function inside which all the components will be listed. We can take a special look at Head component which is a feature in Next.Js though which we can define the title of the application our favicon and other SEO related informations.

about.js​

//  /pages/about.js

import Head from 'next/head';
import React from 'react';
import About from '../components/About';

export default function Home() {

return (
<div>
<Head>
<title>WriteRight | About</title>
<link rel="icon" href="/icon.png" />
</Head>
<About/>
</div>
);
}

This is also simple and same as home page

_app.js​

//  /pages/_app.js

import React from 'react';
import Footer from '../components/Footer';
import Navbar from '../components/Navbar';

function MyApp({ Component, pageProps }) {
return (
<>
<div>
<Navbar />
<main>
<Component {...pageProps} />
</main>
<Footer />
</div>
</>
);
}

export default MyApp;

This file is used in Next.js to define the overall structure of the application. The import statements at the top of the file import the necessary components for the navbar and footer. The MyApp function takes two arguments: Component and pageProps. The Component argument represents the current page being rendered, and the pageProps argument contains any additional properties passed to the page. The MyApp function returns a div element that contains the Navbar component, a main element that contains the current page being rendered, and the Footer component. The <> and </> tags are used to define a fragment in React, which allows us to return multiple elements without having to wrap them in a single parent element. This structure ensures that the navbar and footer are present on all pages of the application.

Now We will look into the most important part of the our application that will be the cover component. It is used to generate the cover letter.

Cover.js​

//   /components/Cover.js

import React from 'react';
import { useState } from 'react';
import styles from '../styles/index.module.css';
import PDFDownloadButton from './PDFDownloadButton';

const Cover = () => {
const [name, setName] = useState('');
const [companyName, setCompanyName] = useState('');
const [role, setRole] = useState('');
const [requirements, setRequirements] = useState('');
const [about, setAbout] = useState('');
const [loading, setLoading] = useState(false);
const [result, setResult] = useState('');

const plainText = result.replace(/<br\s?\/?>/g, '\n');

const copyToClipboard = () => {
const plainText = result.replace(/<br\s?\/?>/g, '\n');
navigator.clipboard.writeText(plainText);
alert('Copied to clipboard!');
};

async function onSubmit(event) {
event.preventDefault();
if (loading) {
return;
}
setLoading(true);
try{
const response = await fetch('/api/generate-cover', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, companyName, role, requirements, about }),
});
const data = await response.json();
setResult(data.result.replaceAll('\n', '<br />'));
} catch (e){
alert("Failed to generate cover letter. Try later");
console.log(e);
} finally {
setLoading(false);
}
}

return (
<div id='generate-cover'>
<main className={styles.main}>
<h3>Cover Letter generator 🔖</h3>
<form onSubmit={onSubmit}>

{/* Label for Name */}
<label>Your Name</label>
<input
type="text"
name="name"
placeholder="Enter Your Name"
value={name}
onChange={(e) => setName(e.target.value)}
/>

<label>About You</label>
<input
type="text"
name="about"
placeholder="Enter about yourself"
value={about}
onChange={(e) => setAbout(e.target.value)}
/>

<label>Company Name</label>
<input
type="text"
name="companyName"
placeholder="Enter Company Name"
value={companyName}
onChange={(e) => setCompanyName(e.target.value)}
/>

<label>Role Applying For</label>
<input
type="text"
name="role"
placeholder="Enter Role you are applying for"
value={role}
onChange={(e) => setRole(e.target.value)}
/>

<label>Company Requirements</label>
<input
type="text"
name="requirements"
placeholder="Enter Company Requirements"
value={requirements}
onChange={(e) => setRequirements(e.target.value)}
/>

<input type="submit" value="Generate cover letter" />
</form>

{loading && (
<div>
<h3>Generating Your Cover Letter...</h3>
<img src="/loading.gif" className={styles.loading} />
</div>
)}

{result && (
<div className={styles.coverresult}>
<div className={styles.coverhead}>
<h2>Your Cover Letter</h2>
</div>
<div className={styles.covertext} dangerouslySetInnerHTML={{ __html: result }} />
<div className={styles.coverbtn}>
<button className={styles.copy} onClick={copyToClipboard}>Copy to Clipboard</button>
<PDFDownloadButton result={plainText} />
</div>
</div>
)}

</main>
</div>
);
}

export default Cover;

This part creates a cover letter based on what the user enters. It uses a CSS module called index.module.css and a PDFDownloadButton component. The Cover function uses React hooks to control the user's input, loading state, and result. When the user sends the form, the onSubmit function sends a POST request to the /api/generate-cover endpoint with the user's input data in the request body.

When it gets a response, the data is stored in the result state. The result state is then shown in the component using dangerouslySetInnerHTML to set the HTML of a div element. The copyToClipboard function is used to copy the plain text of the cover letter to the clipboard. The plainText variable is set to the result state with line breaks replaced by <br /> tags.

PDFDownlaodButton.js​

//   /components/PDFDownlaodButton.js

import { PDFDownloadLink, Document, Page, Text, StyleSheet } from '@react-pdf/renderer';

function PDFDownloadButton({ result }) {
const styles = StyleSheet.create({
page: {
backgroundColor: 'white',
},
text: {
fontSize: 16,
marginBottom: 10,
paddingLeft: 40,
paddingRight: 40,
},
button: {
backgroundColor: '#ff0080',
color: 'white',
border: '0px',
borderRadius: '5px',
padding: '0.5rem 1rem',
textAlign: 'center',
textDecoration: 'none',
cursor: 'pointer',
},
coverhead: {
textAlign: 'center',
color: 'white',
padding: 20,
paddingTop: 40,
paddingBottom: 40,
backgroundColor: '#fe6081',
flexDirection: 'row',
justifyContent: 'space-between',
}
});

const MyDocument = () => (
<Document>
<Page size="A4" style={styles.page}>
<Text style={styles.coverhead}>Cover Letter</Text>
<Text style={styles.text}>{result}</Text>
</Page>
</Document>
);

return (
<PDFDownloadLink document={<MyDocument />} fileName="cover_letter.pdf">
{({ blob, url, loading, error }) =>
<button style={styles.button}>
{loading ? 'Loading document...' : 'Download PDF'}
</button>
}
</PDFDownloadLink>
);
}

export default PDFDownloadButton;

Now just one small thing is left to do and that is defining the pdf download button that will enable us to add download as pdf option in our application

Assets​

In Next.Js application we define the assets inside our public folder. And we can access it from anywhere by using the src=”/icon.png"

Contributing​

Contributions to WriteRight are welcome and encouraged! To contribute to the project, please follow these steps:

  • Fork the repository.
  • Create a new branch for your changes.
  • Make your changes and commit them.
  • Push your changes to your forked repository.
  • Create a pull request to merge your changes into the main repository.
  • Please ensure that your code follows the project's coding standards and that your changes do not introduce any bugs or security vulnerabilities.

License​

WriteRight is open source software licensed under the MIT License. See LICENSE for more information.

INTERVIEW

I Build a project called WRITERIGHT and as the name suggest it helps us to write things rightly .Basically it is a web application that help us to generate cover letters quickly and easily with the help of Artificial Intelligence . For the frontend part i have used React and in the backend part to retrive the cover letter i have used Open AI API . So what the applicaation does is when the user visits the application it asked them few questions like their name , and few lines about them , companny and the role they are applying for. and also tthe requirement of the job. and based on these details it make a request to open ai and gives us back a cover letter . Then user gets the option to copy to clipboard or download in a pdf or word format .

How to fetch API

to fetch the API first we go to open AI website and sign to account and from there we get an option to generate the api key. From our frontend application we make a request to open ai api with a prompt generate a cover letter for ${username} with the details applying for {company name} with job requirement and all this variables we take from the application and insert in this prompt . After the request is made we get a response of cover letter which is shown in the frontend part .

What inspired you

During my summer training i was applying for a lot of companies for internship and in most of the companies it asked for cover letter (like internshala platform) and writing the same cover letter again again was very difficult task so i decided to built this application which provided me personalized cover letter for each company i applied .

challenges you ran into and how did you resolve

There were basically two challenges that i faced first was while letting the user download the cover letter as pdf . So in order to achieve this I look for an external package called pdf render which allows me to take elements from our web page and covert it as a text and let me download as pdf or word format. In their documentation it was written how to use the package .

and second challenge was that Open AI API are not free and just give us one month of free credit. so after one month the api would have stopped working . and since i wanted to make it a complete open source and free to use platform i used several accounts and generate several api keys .

WHy did you choose react

I decided to choose React since it is a very popular framework and very fast for creating single page applications. In React we can divide the website into different components and design each component individually and then integrate it together. And the website is also fast and we can create beautiful UI’s with it easily.

Why not angular? It is too complex to learn and use quickly. Requires lots of code which makes it a bit complicated. • Why not vue? there is no clear best practice in Vue.js in contrast to React, which may make it difficult for major applications to be maintained

Explain your project from scratch how did you build it .

Firstly I decided the structure of the application like how it should be designed and then I planned to divide the application into different components i.e., navbar, then hero section, then the form structure in which we take user input and a footer section. So then I designed each component seperately and then combined it together. Since I wanted to use OpenAI Api’s so i was very clear on how to approach in this website and once the basic layout and designing of my website was done then I implemented the logic to fetch the request.