pvanand commited on
Commit
d0b1be4
·
verified ·
1 Parent(s): 9bd9fbf

Update static/resume-optimizer.html

Browse files
Files changed (1) hide show
  1. static/resume-optimizer.html +200 -201
static/resume-optimizer.html CHANGED
@@ -4,12 +4,17 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Resume Optimizer</title>
 
 
7
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
8
  <style>
 
 
 
 
9
  :root {
10
  --primary-color: #003366;
11
  --secondary-color: #f0f2f5;
12
- --accent-color: #ff6b6b;
13
  }
14
  body {
15
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
@@ -19,10 +24,6 @@
19
  background-color: var(--secondary-color);
20
  color: #333;
21
  }
22
- .container {
23
- max-width: 1200px;
24
- margin: 0 auto;
25
- }
26
  h1, h2 {
27
  color: var(--primary-color);
28
  }
@@ -30,227 +31,225 @@
30
  text-align: center;
31
  margin-bottom: 30px;
32
  }
33
- form {
34
- background: #ffffff;
35
- padding: 20px;
36
- border-radius: 8px;
37
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
38
- margin-bottom: 30px;
39
- display: flex;
40
- flex-wrap: wrap;
41
- gap: 20px;
42
- align-items: flex-start;
43
- }
44
- .form-group {
45
- flex: 1 1 calc(50% - 10px);
46
- min-width: 250px;
47
- }
48
- label {
49
- display: block;
50
- margin-bottom: 8px;
51
- color: var(--primary-color);
52
- font-weight: bold;
53
- }
54
- input[type="text"], input[type="file"], textarea {
55
- width: 100%;
56
  padding: 10px;
57
- margin-bottom: 10px;
58
- border: 1px solid #ccc;
59
- border-radius: 4px;
60
- font-size: 14px;
61
- transition: border-color 0.3s ease;
62
- box-sizing: border-box;
63
- }
64
- input[type="text"]:focus, textarea:focus {
65
- border-color: var(--accent-color);
66
- outline: none;
67
  }
68
- textarea {
69
- height: 150px;
70
- resize: vertical;
 
 
 
 
 
71
  }
72
- .button-container {
73
- flex-basis: 100%;
74
- display: flex;
75
- justify-content: center;
76
- margin-top: 20px;
77
- }
78
- button {
79
- background: var(--primary-color);
80
- color: #fff;
81
- padding: 10px 20px;
82
- border: none;
83
- border-radius: 4px;
84
- cursor: pointer;
85
- font-size: 16px;
86
- transition: background-color 0.3s ease;
87
- width: auto;
88
- min-width: 200px;
89
  }
90
- button:hover {
91
- background: var(--accent-color);
92
  }
93
- .report-section {
94
- background-color: #ffffff;
95
- padding: 40px;
96
- border-radius: 8px;
97
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
98
- margin-top: 30px;
99
- display: none;
100
  }
101
- .report-section h2 {
102
- border-bottom: 2px solid var(--accent-color);
103
- padding-bottom: 10px;
104
- margin-top: 0;
105
  }
106
- .markdown-body {
107
- font-size: 14px;
108
- line-height: 1.6;
109
  }
110
- pre {
111
- background-color: #f6f8fa;
112
- border-radius: 4px;
113
- padding: 15px;
114
- overflow-x: auto;
115
- font-size: 12px;
116
- }
117
- #loading {
118
- text-align: center;
119
- display: none;
120
  padding: 20px;
121
- }
122
- .spinner {
123
- display: inline-block;
124
- width: 50px;
125
- height: 50px;
126
- border: 3px solid rgba(0,51,102,.3);
127
- border-radius: 50%;
128
- border-top-color: var(--primary-color);
129
- animation: spin 1s ease-in-out infinite;
130
- -webkit-animation: spin 1s ease-in-out infinite;
131
- }
132
- @keyframes spin {
133
- to { -webkit-transform: rotate(360deg); }
134
- }
135
- @-webkit-keyframes spin {
136
- to { -webkit-transform: rotate(360deg); }
137
- }
138
- .loading-text {
139
- margin-top: 10px;
140
- font-weight: bold;
141
- color: var(--primary-color);
142
- }
143
- @media (max-width: 768px) {
144
- .form-group {
145
- flex: 1 1 100%;
146
- }
147
  }
148
  </style>
149
  </head>
150
  <body>
151
- <div class="container">
152
- <h1>Resume Optimizer</h1>
153
- <form id="optimizeForm">
154
- <div class="form-group">
155
- <label for="resumeText">Resume:</label>
156
- <textarea id="resumeText" placeholder="Paste your resume here"></textarea>
157
- <input type="file" id="resumeFile" accept=".txt,.pdf,.doc,.docx">
158
- </div>
159
- <div class="form-group">
160
- <label for="jobDescriptionText">Job Description:</label>
161
- <textarea id="jobDescriptionText" placeholder="Paste the job description here"></textarea>
162
- <input type="text" id="jobDescriptionUrl" placeholder="Or enter job description URL">
163
- </div>
164
- <div class="button-container">
165
- <button type="submit">Optimize Resume</button>
166
- </div>
167
- </form>
168
- <div id="loading">
 
169
  <div class="spinner"></div>
170
- <div class="loading-text">Optimizing your resume...</div>
171
  </div>
172
- <div id="optimizedResumeContainer" class="report-section"></div>
173
- <div id="changesMadeContainer" class="report-section"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  </div>
175
 
176
  <script>
177
- const AUTH_TOKEN = "44d5c2ac18ced6fc25c1e57dcd06fc0b31fb4ad97bf56e67540671a647465df4";
178
 
179
- document.getElementById('optimizeForm').addEventListener('submit', async (e) => {
180
- e.preventDefault();
181
- const optimizedResumeContainer = document.getElementById('optimizedResumeContainer');
182
- const changesMadeContainer = document.getElementById('changesMadeContainer');
183
- const loadingDiv = document.getElementById('loading');
184
-
185
- // Hide result containers and show loading animation
186
- optimizedResumeContainer.style.display = 'none';
187
- changesMadeContainer.style.display = 'none';
188
- loadingDiv.style.display = 'block';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
- const formData = new FormData();
 
 
 
 
 
 
 
 
 
191
 
192
- // Handle resume input
193
- const resumeText = document.getElementById('resumeText').value;
194
- const resumeFile = document.getElementById('resumeFile').files[0];
195
- if (resumeText) {
196
- formData.append('resumeText', resumeText);
197
- } else if (resumeFile) {
198
- formData.append('resume', resumeFile);
199
- } else {
200
- loadingDiv.innerHTML = '<div class="loading-text" style="color: red;">Please provide a resume (text or file).</div>';
201
- return;
202
- }
203
 
204
- // Handle job description input
205
- const jobDescriptionText = document.getElementById('jobDescriptionText').value;
206
- const jobDescriptionUrl = document.getElementById('jobDescriptionUrl').value;
207
- if (jobDescriptionText) {
208
- formData.append('jobDescription', jobDescriptionText);
209
- } else if (jobDescriptionUrl) {
210
- formData.append('jobDescriptionUrl', jobDescriptionUrl);
211
- } else {
212
- loadingDiv.innerHTML = '<div class="loading-text" style="color: red;">Please provide a job description (text or URL).</div>';
213
- return;
214
- }
215
 
216
- try {
217
- const response = await fetch('https://pvanand-specialized-agents.hf.space/api/v1/optimize-resume', {
218
- method: 'POST',
219
- headers: {
220
- 'Authorization': `Bearer ${AUTH_TOKEN}`
221
- },
222
- body: formData
223
- });
224
-
225
- if (!response.ok) {
226
- throw new Error(`HTTP error! status: ${response.status}`);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
  }
228
-
229
- const data = await response.json();
230
-
231
- const optimizedResume = data.optimized_resume || 'Optimized resume not found.';
232
- const changesMade = data.changes_made || 'Changes not found.';
233
-
234
- optimizedResumeContainer.innerHTML = `
235
- <h2>Optimized Resume</h2>
236
- <div class="markdown-body">${marked.parse(optimizedResume)}</div>
237
- `;
238
-
239
- changesMadeContainer.innerHTML = `
240
- <h2>Changes Made</h2>
241
- <div class="markdown-body">${marked.parse(changesMade)}</div>
242
- `;
243
-
244
- // Hide loading animation and show result containers
245
- loadingDiv.style.display = 'none';
246
- optimizedResumeContainer.style.display = 'block';
247
- changesMadeContainer.style.display = 'block';
248
- } catch (error) {
249
- loadingDiv.innerHTML = `
250
- <div class="loading-text" style="color: red;">Error: ${error.message}</div>
251
- `;
252
  }
253
- });
254
  </script>
255
  </body>
256
  </html>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Resume Optimizer</title>
7
+ <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
8
+ <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
9
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
10
  <style>
11
+ body {
12
+ background-color: #f0f2f5;
13
+ font-family: 'Arial', sans-serif;
14
+ }
15
  :root {
16
  --primary-color: #003366;
17
  --secondary-color: #f0f2f5;
 
18
  }
19
  body {
20
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 
24
  background-color: var(--secondary-color);
25
  color: #333;
26
  }
 
 
 
 
27
  h1, h2 {
28
  color: var(--primary-color);
29
  }
 
31
  text-align: center;
32
  margin-bottom: 30px;
33
  }
34
+ .container {
35
+ max-width: 800px;
36
+ margin: 30px auto;
37
+ background-color: #ffffff;
38
+ padding: 30px;
39
+ border-radius: 10px;
40
+ box-shadow: 0 0 20px rgba(0,0,0,0.1);
41
+ }
42
+ textarea {
43
+ width: 100%;
44
+ min-height: 150px;
45
+ border: 1px solid #ced4da;
46
+ border-radius: 5px;
 
 
 
 
 
 
 
 
 
 
47
  padding: 10px;
 
 
 
 
 
 
 
 
 
 
48
  }
49
+ .spinner {
50
+ border: 4px solid #f3f3f3;
51
+ border-top: 4px solid #3498db;
52
+ border-radius: 50%;
53
+ width: 40px;
54
+ height: 40px;
55
+ animation: spin 1s linear infinite;
56
+ margin: 0 auto;
57
  }
58
+ @keyframes spin {
59
+ 0% { transform: rotate(0deg); }
60
+ 100% { transform: rotate(360deg); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  }
62
+ .btn-primary, .btn-success, .btn-info {
63
+ transition: all 0.3s ease;
64
  }
65
+ .btn-primary:hover, .btn-success:hover, .btn-info:hover {
66
+ transform: translateY(-2px);
67
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
 
 
 
 
68
  }
69
+ .fade-enter-active, .fade-leave-active {
70
+ transition: opacity 0.5s;
 
 
71
  }
72
+ .fade-enter-from, .fade-leave-to {
73
+ opacity: 0;
 
74
  }
75
+ .optimized-content {
76
+ background-color: #f8f9fa;
77
+ border-radius: 5px;
 
 
 
 
 
 
 
78
  padding: 20px;
79
+ margin-top: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  }
81
  </style>
82
  </head>
83
  <body>
84
+ <div id="app" class="container">
85
+ <h1 class="text-center mb-4">Resume Optimizer</h1>
86
+ <transition name="fade">
87
+ <form v-if="!optimizedResume" @submit.prevent="optimizeResume">
88
+ <div class="mb-3">
89
+ <label for="resumeText" class="form-label">Resume:</label>
90
+ <textarea id="resumeText" v-model="resumeText" class="form-control" placeholder="Paste your resume here"></textarea>
91
+ <input type="file" @change="handleResumeFile" class="form-control mt-2" accept=".txt,.pdf,.doc,.docx">
92
+ </div>
93
+ <div class="mb-3">
94
+ <label for="jobDescriptionText" class="form-label">Job Description:</label>
95
+ <textarea id="jobDescriptionText" v-model="jobDescriptionText" class="form-control" placeholder="Paste the job description here"></textarea>
96
+ <input type="text" v-model="jobDescriptionUrl" class="form-control mt-2" placeholder="Or enter job description URL">
97
+ </div>
98
+ <button type="submit" class="btn btn-primary w-100" :disabled="isLoading">Optimize Resume</button>
99
+ </form>
100
+ </transition>
101
+
102
+ <div v-if="isLoading" class="text-center mt-4">
103
  <div class="spinner"></div>
104
+ <p>Optimizing your resume...</p>
105
  </div>
106
+
107
+ <transition name="fade">
108
+ <div v-if="optimizedResume" class="mt-4">
109
+ <h3 class="mb-3">Optimized Resume</h2>
110
+ <div class="optimized-content" v-html="markedOptimizedResume"></div>
111
+ <div class="mt-3">
112
+ <button @click="downloadPdf" class="btn btn-success me-2">Download as PDF</button>
113
+ <button @click="downloadDocx" class="btn btn-info">Download as DOCX</button>
114
+ <button @click="resetForm" class="btn btn-outline-secondary ms-2">Start Over</button>
115
+ </div>
116
+ </div>
117
+ </transition>
118
+
119
+ <transition name="fade">
120
+ <div v-if="changesMade" class="mt-4">
121
+ <h3 class="mb-3">Changes Made</h2>
122
+ <div class="optimized-content" v-html="markedChangesMade"></div>
123
+ </div>
124
+ </transition>
125
+
126
+ <div v-if="errorMessage" class="alert alert-danger mt-4">{{ errorMessage }}</div>
127
  </div>
128
 
129
  <script>
130
+ const { createApp } = Vue;
131
 
132
+ createApp({
133
+ data() {
134
+ return {
135
+ resumeText: '',
136
+ resumeFile: null,
137
+ jobDescriptionText: '',
138
+ jobDescriptionUrl: '',
139
+ optimizedResume: '',
140
+ changesMade: '',
141
+ isLoading: false,
142
+ errorMessage: '',
143
+ AUTH_TOKEN: "44d5c2ac18ced6fc25c1e57dcd06fc0b31fb4ad97bf56e67540671a647465df4"
144
+ }
145
+ },
146
+ computed: {
147
+ markedOptimizedResume() {
148
+ return marked.parse(this.optimizedResume);
149
+ },
150
+ markedChangesMade() {
151
+ return marked.parse(this.changesMade);
152
+ }
153
+ },
154
+ methods: {
155
+ async optimizeResume() {
156
+ this.isLoading = true;
157
+ this.errorMessage = '';
158
 
159
+ const formData = new FormData();
160
+ if (this.resumeFile) {
161
+ formData.append('resume', this.resumeFile);
162
+ } else if (this.resumeText) {
163
+ formData.append('resumeText', this.resumeText);
164
+ } else {
165
+ this.errorMessage = 'Please provide a resume (text or file).';
166
+ this.isLoading = false;
167
+ return;
168
+ }
169
 
170
+ if (this.jobDescriptionText) {
171
+ formData.append('jobDescription', this.jobDescriptionText);
172
+ } else if (this.jobDescriptionUrl) {
173
+ formData.append('jobDescriptionUrl', this.jobDescriptionUrl);
174
+ } else {
175
+ this.errorMessage = 'Please provide a job description (text or URL).';
176
+ this.isLoading = false;
177
+ return;
178
+ }
 
 
179
 
180
+ try {
181
+ const response = await fetch('https://pvanand-specialized-agents.hf.space/api/v1/optimize-resume', {
182
+ method: 'POST',
183
+ headers: { 'Authorization': `Bearer ${this.AUTH_TOKEN}` },
184
+ body: formData
185
+ });
186
+
187
+ if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
 
 
 
188
 
189
+ const data = await response.json();
190
+ this.optimizedResume = data.optimized_resume || 'Optimized resume not found.';
191
+ this.changesMade = data.changes_made || 'Changes not found.';
192
+ } catch (error) {
193
+ this.errorMessage = `Error: ${error.message}`;
194
+ } finally {
195
+ this.isLoading = false;
196
+ }
197
+ },
198
+ handleResumeFile(event) {
199
+ const file = event.target.files[0];
200
+ if (file) {
201
+ this.resumeFile = file;
202
+ if (file.type === 'application/pdf') {
203
+ this.resumeText = `[PDF File: ${file.name}]`;
204
+ } else {
205
+ const reader = new FileReader();
206
+ reader.onload = (e) => {
207
+ this.resumeText = e.target.result;
208
+ };
209
+ reader.readAsText(file);
210
+ }
211
+ }
212
+ },
213
+ async downloadPdf() {
214
+ await this.downloadFile('https://pvanand-web-scraping.hf.space/html_to_pdf', 'html_content', 'optimized_resume.pdf');
215
+ },
216
+ async downloadDocx() {
217
+ await this.downloadFile('https://pvanand-web-scraping.hf.space/convert', 'html', 'optimized_resume.docx');
218
+ },
219
+ async downloadFile(url, paramName, filename) {
220
+ try {
221
+ const response = await fetch(url, {
222
+ method: 'POST',
223
+ headers: { 'Content-Type': 'application/json' },
224
+ body: JSON.stringify({ [paramName]: this.markedOptimizedResume })
225
+ });
226
+
227
+ if (!response.ok) throw new Error('Conversion failed');
228
+
229
+ const blob = await response.blob();
230
+ const downloadUrl = window.URL.createObjectURL(blob);
231
+ const a = document.createElement('a');
232
+ a.style.display = 'none';
233
+ a.href = downloadUrl;
234
+ a.download = filename;
235
+ document.body.appendChild(a);
236
+ a.click();
237
+ window.URL.revokeObjectURL(downloadUrl);
238
+ } catch (error) {
239
+ this.errorMessage = `Failed to download ${filename}. Please try again.`;
240
+ }
241
+ },
242
+ resetForm() {
243
+ this.resumeText = '';
244
+ this.resumeFile = null;
245
+ this.jobDescriptionText = '';
246
+ this.jobDescriptionUrl = '';
247
+ this.optimizedResume = '';
248
+ this.changesMade = '';
249
+ this.errorMessage = '';
250
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  }
252
+ }).mount('#app');
253
  </script>
254
  </body>
255
  </html>