set up downloads for tailored resume and cover letter

This commit is contained in:
Kyle Belanger 2025-02-20 15:57:38 -05:00
parent 099ccc40e8
commit 8efa97d950
3 changed files with 193 additions and 46 deletions

View file

@ -32,11 +32,12 @@
<div id="coverLetterSection">
<h3>Generated Cover Letter:</h3>
<textarea id="coverLetterOutput" rows="15" cols="80"></textarea>
<button id="downloadBtn">Download as DOCX</button>
<button id="downloadCoverLetterBtn">Download as DOCX</button>
</div>
<div id="tailoredResumeSection">
<h3>Tailored Resume:</h3>
<textarea id="tailoredResumeOutput" rows="15" cols="80"></textarea>
<button id="downloadResumeBtn">Download Resume as DOCX</button>
<h3>Key Updates</h3>
<pre id="keyResumeUpdates"></pre>
</div>

View file

@ -115,14 +115,13 @@ 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', {
const response = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ coverLetterText })
body: JSON.stringify({ content })
});
if (!response.ok) throw new Error('Failed to download document.');
@ -134,7 +133,7 @@ document.getElementById('downloadBtn').addEventListener('click', async function
// Create a temporary download link
const a = document.createElement('a');
a.href = url;
a.download = 'cover_letter.docx';
a.download = filename;
document.body.appendChild(a);
a.click();
@ -145,8 +144,60 @@ document.getElementById('downloadBtn').addEventListener('click', async function
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

View file

@ -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 =>
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(text),
new TextRun("\n") // Ensures spacing between paragraphs
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;