set up downloads for tailored resume and cover letter
This commit is contained in:
parent
099ccc40e8
commit
8efa97d950
3 changed files with 193 additions and 46 deletions
|
@ -32,11 +32,12 @@
|
||||||
<div id="coverLetterSection">
|
<div id="coverLetterSection">
|
||||||
<h3>Generated Cover Letter:</h3>
|
<h3>Generated Cover Letter:</h3>
|
||||||
<textarea id="coverLetterOutput" rows="15" cols="80"></textarea>
|
<textarea id="coverLetterOutput" rows="15" cols="80"></textarea>
|
||||||
<button id="downloadBtn">Download as DOCX</button>
|
<button id="downloadCoverLetterBtn">Download as DOCX</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="tailoredResumeSection">
|
<div id="tailoredResumeSection">
|
||||||
<h3>Tailored Resume:</h3>
|
<h3>Tailored Resume:</h3>
|
||||||
<textarea id="tailoredResumeOutput" rows="15" cols="80"></textarea>
|
<textarea id="tailoredResumeOutput" rows="15" cols="80"></textarea>
|
||||||
|
<button id="downloadResumeBtn">Download Resume as DOCX</button>
|
||||||
<h3>Key Updates</h3>
|
<h3>Key Updates</h3>
|
||||||
<pre id="keyResumeUpdates"></pre>
|
<pre id="keyResumeUpdates"></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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 {
|
try {
|
||||||
const response = await fetch('/generate/download', {
|
const response = await fetch(endpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ coverLetterText })
|
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
|
// Convert response to blob
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
const url = window.URL.createObjectURL(blob);
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
|
||||||
// Create a temporary download link
|
// Create a temporary download link
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = url;
|
a.href = url;
|
||||||
a.download = 'cover_letter.docx';
|
a.download = filename;
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
window.URL.revokeObjectURL(url);
|
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.');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
function formatCoverLetter(rawText) {
|
||||||
return rawText
|
return rawText
|
||||||
|
|
|
@ -95,39 +95,102 @@ router.post('/', async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
router.post('/download', async (req, res) => {
|
// router.post('/download', async (req, res) => {
|
||||||
try {
|
// try {
|
||||||
const { coverLetterText } = req.body;
|
// const { coverLetterText } = req.body;
|
||||||
|
|
||||||
// Generate .docx file dynamically
|
// // Generate .docx file dynamically
|
||||||
const docBuffer = await generateCoverLetter(coverLetterText);
|
// const docBuffer = await generateCoverLetter(coverLetterText);
|
||||||
|
|
||||||
// Set response headers for file download
|
// // Set response headers for file download
|
||||||
res.setHeader('Content-Disposition', 'attachment; filename="cover_letter.docx"');
|
// res.setHeader('Content-Disposition', 'attachment; filename="cover_letter.docx"');
|
||||||
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
|
// res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
|
||||||
|
|
||||||
res.send(docBuffer);
|
// res.send(docBuffer);
|
||||||
|
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.error(error);
|
// console.error(error);
|
||||||
res.status(500).json({ error: 'Error generating document' });
|
// 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") {
|
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 = [];
|
||||||
const paragraphs = rawText.split("\n\n").map(text =>
|
|
||||||
new Paragraph({
|
// Split text into lines
|
||||||
children: [
|
rawText.split("\n").forEach(line => {
|
||||||
new TextRun(text),
|
if (line.trim().startsWith("•")) {
|
||||||
new TextRun("\n") // Ensures spacing between paragraphs
|
// 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
|
// Create the document
|
||||||
const doc = new 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;
|
module.exports = router;
|
Loading…
Reference in a new issue