update js files to swith from using upload to

local storage
This commit is contained in:
Kyle Belanger 2025-02-14 16:02:34 -05:00
parent eff0716078
commit 9ef73adffb
2 changed files with 115 additions and 33 deletions

View file

@ -1,7 +1,6 @@
document.getElementById('uploadForm').addEventListener('submit', async function (event) { document.getElementById('uploadForm').addEventListener('submit', async function (event) {
event.preventDefault(); event.preventDefault();
const formData = new FormData(this);
const outputSection = document.getElementById('outputSection'); const outputSection = document.getElementById('outputSection');
const coverLetterOutput = document.getElementById('coverLetterOutput'); const coverLetterOutput = document.getElementById('coverLetterOutput');
const generateBtn = document.getElementById('generateBtn'); const generateBtn = document.getElementById('generateBtn');
@ -16,10 +15,36 @@ document.getElementById('uploadForm').addEventListener('submit', async function
outputSection.style.display = "block"; outputSection.style.display = "block";
coverLetterOutput.value = "Generating cover letter..."; coverLetterOutput.value = "Generating cover letter...";
const fileInput = document.getElementById('resume');
const jobDescriptionInput = document.getElementById('jobDescription');
if (!fileInput || !jobDescriptionInput) {
console.error("Form elements not found.");
return;
}
const file = fileInput.files[0];
const jobDescription = jobDescriptionInput.value;
if (!file) {
alert("Please upload a resume.");
return;
}
if (!jobDescription.trim()) {
alert("Please enter a job description.");
return;
}
const formData = new FormData();
formData.append("resume", file);
formData.append("jobDescription", jobDescription);
try { try {
const response = await fetch('/generate', { const response = await fetch('/generate', {
method: 'POST', method: "POST",
body: formData body: formData,
}); });
const result = await response.json(); const result = await response.json();
@ -41,7 +66,7 @@ document.getElementById('uploadForm').addEventListener('submit', async function
}); });
document.getElementById('downloadBtn').addEventListener('click', async function() { document.getElementById('downloadBtn').addEventListener('click', async function () {
const coverLetterText = document.getElementById('coverLetterOutput').value; const coverLetterText = document.getElementById('coverLetterOutput').value;
try { try {
@ -51,15 +76,24 @@ document.getElementById('downloadBtn').addEventListener('click', async function(
body: JSON.stringify({ coverLetterText }) body: JSON.stringify({ coverLetterText })
}); });
const result = await response.json(); if (!response.ok) throw new Error('Failed to download document.');
if (result.downloadLink) {
window.location.href = result.downloadLink; // Convert response to blob
} else { const blob = await response.blob();
alert('Error downloading document.') const url = window.URL.createObjectURL(blob);
}
// Create a temporary download link
const a = document.createElement('a');
a.href = url;
a.download = 'cover_letter.docx';
document.body.appendChild(a);
a.click();
// Cleanup
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
} catch (error) { } catch (error) {
console.error('Error:', error); console.error('Error:', error);
alert('Failed to download document.') alert('Failed to download document.');
} }
}); });

View file

@ -3,17 +3,13 @@ const express = require('express');
const multer = require('multer'); const multer = require('multer');
const pdf = require('pdf-parse'); const pdf = require('pdf-parse');
const { Document, Packer, Paragraph, TextRun } = require('docx'); const { Document, Packer, Paragraph, TextRun } = require('docx');
const fs = require('fs');
const promises = require('fs/promises');
const Anthropic = require('@anthropic-ai/sdk'); const Anthropic = require('@anthropic-ai/sdk');
const path = require('path');
const { runInNewContext } = require('vm');
// Load Environment Variables // Load Environment Variables
require('dotenv').config(); require('dotenv').config();
const router = express.Router(); const router = express.Router();
const upload = multer({ dest: 'uploads/'}); const upload = multer({ storage: multer.memoryStorage() });
// initialize LLM // initialize LLM
//TODO Update to have User Provide their own key //TODO Update to have User Provide their own key
@ -25,21 +21,27 @@ const anthropic = new Anthropic({
// Handle Resume upload and user input for Job Description // Handle Resume upload and user input for Job Description
router.post('/', upload.single('resume'), async (req, res) =>{ router.post('/', upload.single('resume'), async (req, res) =>{
try{ try{
const resumePath = req.file.path;
if (!req.file || !req.body.jobDescription) {
return res.status(400).json({ error: 'Resume and Job Description are required' });
}
let resumeText = req.file.buffer.toString('utf-8');
const jobDescription = req.body.jobDescription; const jobDescription = req.body.jobDescription;
if (req.file.mimetype === 'application/pdf') {
const pdfData = await pdf(req.file.buffer);
resumeText = pdfData.text;
}
// Load the LLM APi Messages // Load the LLM APi Messages
// These include placeholders to be replaced later in function // These include placeholders to be replaced later in function
const resume_parser_api = require('../data/resume_parser_api.json'); const resume_parser_api = require('../data/resume_parser_api.json');
const cover_letter_api = require('../data/cover_letter_api.json'); const cover_letter_api = require('../data/cover_letter_api.json');
// Extract Data from PDF (includes text and meta data if needed for later development)
const resumeData = await promises.readFile(resumePath);
const extractedResumeText = await pdf(resumeData);
// Replace placeholder in resume api with extracted text // Replace placeholder in resume api with extracted text
resume_parser_api.messages[0].content[0].text = resume_parser_api.messages[0].content[0].text.replace("{{resume}}", extractedResumeText.text); resume_parser_api.messages[0].content[0].text = resume_parser_api.messages[0].content[0].text.replace("{{resume}}", resumeText);
// Send resume to LLM // Send resume to LLM
const resumeResponse = await anthropic.messages.create(resume_parser_api); const resumeResponse = await anthropic.messages.create(resume_parser_api);
@ -64,7 +66,7 @@ router.post('/', upload.single('resume'), async (req, res) =>{
router.post('/download', async (req, res) => { router.post('/download', async (req, res) => {
try { try {
const { coverLetterText } = res.body; const { coverLetterText } = req.body;
const outputFilename = path.join(__dirname, '../uploads/cover_letter.docx'); const outputFilename = path.join(__dirname, '../uploads/cover_letter.docx');
generateCoverLetter(coverLetterText, outputFilename); generateCoverLetter(coverLetterText, outputFilename);
@ -77,10 +79,56 @@ router.post('/download', async (req, res) => {
}); });
function generateCoverLetter(rawText, outputFilename) { function generateCoverLetter(rawText, outputFilename) {
const header = rawText.match(/<header>(.*?)<\/header>/s)[1].trim();
const greeting = rawText.match(/<greeting>(.*?)<\/greeting>/s)[1].trim();
const introduction = rawText.match(/<introduction>(.*?)<\/introduction>/s)[1].trim();
const body = rawText.match(/<body>(.*?)<\/body>/s)[1].trim();
const conclusion = rawText.match(/<conclusion>(.*?)<\/conclusion>/s)[1].trim();
const signature = rawText.match(/<signature>(.*?)<\/signature>/s)[1].trim();
// Create a new document using docx
const doc = new Document({ const doc = new Document({
sections: [ sections: [
{ {
children: [new Paragraph({ children: [new TextRun(rawText)] })], properties: {},
children: [
new Paragraph({
children: [
new TextRun(header),
new TextRun("\n\n"), // Add line breaks between sections
],
}),
new Paragraph({
children: [
new TextRun(greeting),
new TextRun("\n\n"),
],
}),
new Paragraph({
children: [
new TextRun(introduction),
new TextRun("\n\n"),
],
}),
new Paragraph({
children: [
new TextRun(body),
new TextRun("\n\n"),
],
}),
new Paragraph({
children: [
new TextRun(conclusion),
new TextRun("\n\n"),
],
}),
new Paragraph({
children: [
new TextRun(signature),
new TextRun("\n\n"),
],
}),
],
}, },
], ],
}); });