specialized-agents / static /resume-optimizer.html
pvanand's picture
Update static/resume-optimizer.html
d0b1be4 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Resume Optimizer</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
body {
background-color: #f0f2f5;
font-family: 'Arial', sans-serif;
}
:root {
--primary-color: #003366;
--secondary-color: #f0f2f5;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background-color: var(--secondary-color);
color: #333;
}
h1, h2 {
color: var(--primary-color);
}
h1 {
text-align: center;
margin-bottom: 30px;
}
.container {
max-width: 800px;
margin: 30px auto;
background-color: #ffffff;
padding: 30px;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0,0,0,0.1);
}
textarea {
width: 100%;
min-height: 150px;
border: 1px solid #ced4da;
border-radius: 5px;
padding: 10px;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.btn-primary, .btn-success, .btn-info {
transition: all 0.3s ease;
}
.btn-primary:hover, .btn-success:hover, .btn-info:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
.optimized-content {
background-color: #f8f9fa;
border-radius: 5px;
padding: 20px;
margin-top: 20px;
}
</style>
</head>
<body>
<div id="app" class="container">
<h1 class="text-center mb-4">Resume Optimizer</h1>
<transition name="fade">
<form v-if="!optimizedResume" @submit.prevent="optimizeResume">
<div class="mb-3">
<label for="resumeText" class="form-label">Resume:</label>
<textarea id="resumeText" v-model="resumeText" class="form-control" placeholder="Paste your resume here"></textarea>
<input type="file" @change="handleResumeFile" class="form-control mt-2" accept=".txt,.pdf,.doc,.docx">
</div>
<div class="mb-3">
<label for="jobDescriptionText" class="form-label">Job Description:</label>
<textarea id="jobDescriptionText" v-model="jobDescriptionText" class="form-control" placeholder="Paste the job description here"></textarea>
<input type="text" v-model="jobDescriptionUrl" class="form-control mt-2" placeholder="Or enter job description URL">
</div>
<button type="submit" class="btn btn-primary w-100" :disabled="isLoading">Optimize Resume</button>
</form>
</transition>
<div v-if="isLoading" class="text-center mt-4">
<div class="spinner"></div>
<p>Optimizing your resume...</p>
</div>
<transition name="fade">
<div v-if="optimizedResume" class="mt-4">
<h3 class="mb-3">Optimized Resume</h2>
<div class="optimized-content" v-html="markedOptimizedResume"></div>
<div class="mt-3">
<button @click="downloadPdf" class="btn btn-success me-2">Download as PDF</button>
<button @click="downloadDocx" class="btn btn-info">Download as DOCX</button>
<button @click="resetForm" class="btn btn-outline-secondary ms-2">Start Over</button>
</div>
</div>
</transition>
<transition name="fade">
<div v-if="changesMade" class="mt-4">
<h3 class="mb-3">Changes Made</h2>
<div class="optimized-content" v-html="markedChangesMade"></div>
</div>
</transition>
<div v-if="errorMessage" class="alert alert-danger mt-4">{{ errorMessage }}</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
resumeText: '',
resumeFile: null,
jobDescriptionText: '',
jobDescriptionUrl: '',
optimizedResume: '',
changesMade: '',
isLoading: false,
errorMessage: '',
AUTH_TOKEN: "44d5c2ac18ced6fc25c1e57dcd06fc0b31fb4ad97bf56e67540671a647465df4"
}
},
computed: {
markedOptimizedResume() {
return marked.parse(this.optimizedResume);
},
markedChangesMade() {
return marked.parse(this.changesMade);
}
},
methods: {
async optimizeResume() {
this.isLoading = true;
this.errorMessage = '';
const formData = new FormData();
if (this.resumeFile) {
formData.append('resume', this.resumeFile);
} else if (this.resumeText) {
formData.append('resumeText', this.resumeText);
} else {
this.errorMessage = 'Please provide a resume (text or file).';
this.isLoading = false;
return;
}
if (this.jobDescriptionText) {
formData.append('jobDescription', this.jobDescriptionText);
} else if (this.jobDescriptionUrl) {
formData.append('jobDescriptionUrl', this.jobDescriptionUrl);
} else {
this.errorMessage = 'Please provide a job description (text or URL).';
this.isLoading = false;
return;
}
try {
const response = await fetch('https://pvanand-specialized-agents.hf.space/api/v1/optimize-resume', {
method: 'POST',
headers: { 'Authorization': `Bearer ${this.AUTH_TOKEN}` },
body: formData
});
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
this.optimizedResume = data.optimized_resume || 'Optimized resume not found.';
this.changesMade = data.changes_made || 'Changes not found.';
} catch (error) {
this.errorMessage = `Error: ${error.message}`;
} finally {
this.isLoading = false;
}
},
handleResumeFile(event) {
const file = event.target.files[0];
if (file) {
this.resumeFile = file;
if (file.type === 'application/pdf') {
this.resumeText = `[PDF File: ${file.name}]`;
} else {
const reader = new FileReader();
reader.onload = (e) => {
this.resumeText = e.target.result;
};
reader.readAsText(file);
}
}
},
async downloadPdf() {
await this.downloadFile('https://pvanand-web-scraping.hf.space/html_to_pdf', 'html_content', 'optimized_resume.pdf');
},
async downloadDocx() {
await this.downloadFile('https://pvanand-web-scraping.hf.space/convert', 'html', 'optimized_resume.docx');
},
async downloadFile(url, paramName, filename) {
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ [paramName]: this.markedOptimizedResume })
});
if (!response.ok) throw new Error('Conversion failed');
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(downloadUrl);
} catch (error) {
this.errorMessage = `Failed to download ${filename}. Please try again.`;
}
},
resetForm() {
this.resumeText = '';
this.resumeFile = null;
this.jobDescriptionText = '';
this.jobDescriptionUrl = '';
this.optimizedResume = '';
this.changesMade = '';
this.errorMessage = '';
}
}
}).mount('#app');
</script>
</body>
</html>