From 8efa97d950ebb44e75aca5693a561d2671f9ef00 Mon Sep 17 00:00:00 2001 From: Kyle Belanger Date: Thu, 20 Feb 2025 15:57:38 -0500 Subject: [PATCH] set up downloads for tailored resume and cover letter --- public/index.html | 3 +- public/js/script.js | 91 +++++++++++++++++++++------ routes/generate.js | 145 ++++++++++++++++++++++++++++++++++++-------- 3 files changed, 193 insertions(+), 46 deletions(-) diff --git a/public/index.html b/public/index.html index 45a5eec..50fccdb 100644 --- a/public/index.html +++ b/public/index.html @@ -32,11 +32,12 @@

Generated Cover Letter:

- +

Tailored Resume:

+

Key Updates

 
   
diff --git a/public/js/script.js b/public/js/script.js index 9c66580..124db4c 100644 --- a/public/js/script.js +++ b/public/js/script.js @@ -115,38 +115,89 @@ document.getElementById("generateCoverLetterBtn").addEventListener("click", asyn } }); -document.getElementById('downloadBtn').addEventListener('click', async function () { - const coverLetterText = document.getElementById('coverLetterOutput').value; +async function downloadDocument(content, endpoint, filename) { try { - const response = await fetch('/generate/download', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ coverLetterText }) - }); + const response = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ content }) + }); - if (!response.ok) throw new Error('Failed to download document.'); + if (!response.ok) throw new Error('Failed to download document.'); - // Convert response to blob - const blob = await response.blob(); - const url = window.URL.createObjectURL(blob); + // Convert response to blob + const blob = await response.blob(); + 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(); + // Create a temporary download link + const a = document.createElement('a'); + a.href = url; + a.download = filename; + document.body.appendChild(a); + a.click(); - // Cleanup - document.body.removeChild(a); - window.URL.revokeObjectURL(url); + // Cleanup + document.body.removeChild(a); + window.URL.revokeObjectURL(url); } catch (error) { console.error('Error:', error); alert('Failed to download document.'); } +} + +// Event listener for cover letter download +document.getElementById('downloadCoverLetterBtn').addEventListener('click', function () { + const coverLetterText = document.getElementById('coverLetterOutput').value; + if (!coverLetterText.trim()) { + alert("Cover letter is empty!"); + return; + } + downloadDocument(coverLetterText, '/generate/download-cover-letter', 'cover_letter.docx'); }); +// Event listener for tailored resume download +document.getElementById('downloadResumeBtn').addEventListener('click', function () { + const tailoredResumeText = document.getElementById('tailoredResumeOutput').value; + if (!tailoredResumeText.trim()) { + alert("Tailored resume is empty!"); + return; + } + downloadDocument(tailoredResumeText, '/generate/download-resume', 'tailored_resume.docx'); +}); + +// document.getElementById('downloadBtn').addEventListener('click', async function () { +// const coverLetterText = document.getElementById('coverLetterOutput').value; + +// try { +// const response = await fetch('/generate/download', { +// method: 'POST', +// headers: { 'Content-Type': 'application/json' }, +// body: JSON.stringify({ coverLetterText }) +// }); + +// if (!response.ok) throw new Error('Failed to download document.'); + +// // Convert response to blob +// const blob = await response.blob(); +// 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) { +// console.error('Error:', error); +// alert('Failed to download document.'); +// } +// }); + function formatCoverLetter(rawText) { return rawText diff --git a/routes/generate.js b/routes/generate.js index 34c8bcf..24c80cc 100644 --- a/routes/generate.js +++ b/routes/generate.js @@ -95,39 +95,102 @@ router.post('/', async (req, res) => { } }); -router.post('/download', async (req, res) => { - try { - const { coverLetterText } = req.body; +// router.post('/download', async (req, res) => { +// try { +// const { coverLetterText } = req.body; - // Generate .docx file dynamically - const docBuffer = await generateCoverLetter(coverLetterText); +// // Generate .docx file dynamically +// const docBuffer = await generateCoverLetter(coverLetterText); - // Set response headers for file download - res.setHeader('Content-Disposition', 'attachment; filename="cover_letter.docx"'); - res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); +// // Set response headers for file download +// res.setHeader('Content-Disposition', 'attachment; filename="cover_letter.docx"'); +// res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); - res.send(docBuffer); +// res.send(docBuffer); - } catch (error) { - console.error(error); - res.status(500).json({ error: 'Error generating document' }); - } -}); +// } catch (error) { +// console.error(error); +// res.status(500).json({ error: 'Error generating document' }); +// } +// }); -function generateCoverLetter(rawText) { +// function generateCoverLetter(rawText) { +// if (!rawText || typeof rawText !== "string") { +// throw new Error("Invalid cover letter text provided."); +// } + +// // Convert text into paragraphs, splitting by double newlines +// const paragraphs = rawText.split("\n\n").map(text => +// new Paragraph({ +// children: [ +// new TextRun(text), +// new TextRun("\n") // Ensures spacing between paragraphs +// ] +// }) +// ); + +// // Create the document +// const doc = new Document({ +// sections: [{ properties: {}, children: paragraphs }] +// }); + +// return Packer.toBuffer(doc); +// } + +// Function to generate a DOCX file +// function generateDocx(rawText) { +// if (!rawText || typeof rawText !== "string") { +// throw new Error("Invalid text provided."); +// } + +// const paragraphs = rawText.split("\n\n").map(text => +// new Paragraph({ +// children: [ +// new TextRun(text), +// new TextRun("\n") // Ensures spacing between paragraphs +// ] +// }) +// ); + +// const doc = new Document({ +// sections: [{ properties: {}, children: paragraphs }] +// }); + +// return Packer.toBuffer(doc); +// } + +function generateDocx(rawText) { if (!rawText || typeof rawText !== "string") { - throw new Error("Invalid cover letter text provided."); + throw new Error("Invalid resume text provided."); } - // Convert text into paragraphs, splitting by double newlines - const paragraphs = rawText.split("\n\n").map(text => - new Paragraph({ - children: [ - new TextRun(text), - new TextRun("\n") // Ensures spacing between paragraphs - ] - }) - ); + const paragraphs = []; + + // Split text into lines + rawText.split("\n").forEach(line => { + if (line.trim().startsWith("•")) { + // Format bullet points properly + paragraphs.push( + new Paragraph({ + text: line.trim(), // Keep bullet point text + bullet: { level: 0 } // Apply bullet point formatting + }) + ); + } else if (line.trim() === "") { + // Add spacing for empty lines + paragraphs.push(new Paragraph({ text: "" })); + } else { + // Format normal text + paragraphs.push( + new Paragraph({ + children: [ + new TextRun(line.trim()), + new TextRun("\n") + ] + }) + ); + } + }); // Create the document const doc = new Document({ @@ -138,5 +201,37 @@ function generateCoverLetter(rawText) { } +// Route to download cover letter +router.post('/download-cover-letter', async (req, res) => { + try { + const { content } = req.body; + const docBuffer = await generateDocx(content); + + res.setHeader('Content-Disposition', 'attachment; filename="cover_letter.docx"'); + res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); + + res.send(docBuffer); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Error generating document' }); + } +}); + +// Route to download tailored resume +router.post('/download-resume', async (req, res) => { + try { + const { content } = req.body; + const docBuffer = await generateDocx(content); + + res.setHeader('Content-Disposition', 'attachment; filename="tailored_resume.docx"'); + res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); + + res.send(docBuffer); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Error generating document' }); + } +}); + module.exports = router; \ No newline at end of file