michael-agent 1.0.2__py3-none-any.whl → 1.0.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- michael_agent/dashboard/static/styles.css +311 -0
- michael_agent/dashboard/templates/career_portal.html +482 -0
- michael_agent/dashboard/templates/dashboard.html +807 -0
- michael_agent/dashboard/templates/jd_creation.html +318 -0
- michael_agent/dashboard/templates/resume_scoring.html +1032 -0
- michael_agent/dashboard/templates/upload_resume.html +411 -0
- {michael_agent-1.0.2.dist-info → michael_agent-1.0.4.dist-info}/METADATA +1 -1
- {michael_agent-1.0.2.dist-info → michael_agent-1.0.4.dist-info}/RECORD +10 -4
- {michael_agent-1.0.2.dist-info → michael_agent-1.0.4.dist-info}/WHEEL +0 -0
- {michael_agent-1.0.2.dist-info → michael_agent-1.0.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,318 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>Job Description Creation - SmartRecruitAgent</title>
|
7
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
8
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
9
|
+
<link rel="stylesheet" href="/static/styles.css">
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<header class="bg-dark text-white p-3">
|
13
|
+
<div class="container">
|
14
|
+
<div class="d-flex justify-content-between align-items-center">
|
15
|
+
<h1 class="h3 mb-0">SmartRecruitAgent - Job Description Creator</h1>
|
16
|
+
<div>
|
17
|
+
<a href="/career-portal" class="btn btn-outline-light me-2">Career Portal</a>
|
18
|
+
<a href="/" class="btn btn-outline-light">Dashboard</a>
|
19
|
+
</div>
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
</header>
|
23
|
+
|
24
|
+
<div class="container mt-4">
|
25
|
+
<div class="card mb-4">
|
26
|
+
<div class="card-header bg-primary text-white">
|
27
|
+
Create New Job Description
|
28
|
+
</div>
|
29
|
+
<div class="card-body">
|
30
|
+
<form id="job-description-form">
|
31
|
+
<div class="row mb-3">
|
32
|
+
<div class="col-md-6">
|
33
|
+
<label for="position" class="form-label">Position Title*</label>
|
34
|
+
<input type="text" class="form-control" id="position" name="position" required>
|
35
|
+
</div>
|
36
|
+
<div class="col-md-6">
|
37
|
+
<label for="location" class="form-label">Location*</label>
|
38
|
+
<input type="text" class="form-control" id="location" name="location" required>
|
39
|
+
</div>
|
40
|
+
</div>
|
41
|
+
|
42
|
+
<div class="row mb-3">
|
43
|
+
<div class="col-md-4">
|
44
|
+
<label for="business_area" class="form-label">Business Area*</label>
|
45
|
+
<input type="text" class="form-control" id="business_area" name="business_area" required>
|
46
|
+
</div>
|
47
|
+
<div class="col-md-4">
|
48
|
+
<label for="employment_type" class="form-label">Employment Type*</label>
|
49
|
+
<select class="form-select" id="employment_type" name="employment_type" required>
|
50
|
+
<option value="Full-time">Full-time</option>
|
51
|
+
<option value="Part-time">Part-time</option>
|
52
|
+
<option value="Contract">Contract</option>
|
53
|
+
<option value="Temporary">Temporary</option>
|
54
|
+
<option value="Internship">Internship</option>
|
55
|
+
</select>
|
56
|
+
</div>
|
57
|
+
<div class="col-md-4">
|
58
|
+
<label for="experience_level" class="form-label">Experience Level*</label>
|
59
|
+
<select class="form-select" id="experience_level" name="experience_level" required>
|
60
|
+
<option value="Entry-level">Entry-level</option>
|
61
|
+
<option value="Junior">Junior</option>
|
62
|
+
<option value="Mid-level">Mid-level</option>
|
63
|
+
<option value="Senior">Senior</option>
|
64
|
+
<option value="Executive">Executive</option>
|
65
|
+
</select>
|
66
|
+
</div>
|
67
|
+
</div>
|
68
|
+
|
69
|
+
<div class="row mb-3">
|
70
|
+
<div class="col-md-6">
|
71
|
+
<label for="work_arrangement" class="form-label">Work Arrangement*</label>
|
72
|
+
<select class="form-select" id="work_arrangement" name="work_arrangement" required>
|
73
|
+
<option value="On-site">On-site</option>
|
74
|
+
<option value="Remote">Remote</option>
|
75
|
+
<option value="Hybrid">Hybrid</option>
|
76
|
+
</select>
|
77
|
+
</div>
|
78
|
+
<div class="col-md-6">
|
79
|
+
<label for="salary_range" class="form-label">Salary Range (Optional)</label>
|
80
|
+
<input type="text" class="form-control" id="salary_range" name="salary_range" placeholder="e.g., $80,000 - $100,000">
|
81
|
+
</div>
|
82
|
+
</div>
|
83
|
+
|
84
|
+
<div class="mb-3">
|
85
|
+
<label for="required_skills" class="form-label">Required Skills*</label>
|
86
|
+
<div class="input-group">
|
87
|
+
<input type="text" class="form-control" id="skill-input" placeholder="Add a skill and press Enter">
|
88
|
+
<button class="btn btn-outline-secondary" type="button" id="add-skill-btn">Add</button>
|
89
|
+
</div>
|
90
|
+
<div id="required_skills_container" class="mt-2 d-flex flex-wrap gap-2"></div>
|
91
|
+
<input type="hidden" id="required_skills" name="required_skills" value="[]">
|
92
|
+
</div>
|
93
|
+
|
94
|
+
<div class="mb-3">
|
95
|
+
<label for="preferred_skills" class="form-label">Preferred Skills (Optional)</label>
|
96
|
+
<div class="input-group">
|
97
|
+
<input type="text" class="form-control" id="preferred-skill-input" placeholder="Add a preferred skill and press Enter">
|
98
|
+
<button class="btn btn-outline-secondary" type="button" id="add-preferred-skill-btn">Add</button>
|
99
|
+
</div>
|
100
|
+
<div id="preferred_skills_container" class="mt-2 d-flex flex-wrap gap-2"></div>
|
101
|
+
<input type="hidden" id="preferred_skills" name="preferred_skills" value="[]">
|
102
|
+
</div>
|
103
|
+
|
104
|
+
<div class="mb-3">
|
105
|
+
<label for="application_deadline" class="form-label">Application Deadline (Optional)</label>
|
106
|
+
<input type="date" class="form-control" id="application_deadline" name="application_deadline">
|
107
|
+
</div>
|
108
|
+
|
109
|
+
<div class="text-center">
|
110
|
+
<button type="submit" class="btn btn-primary" id="generate-btn">
|
111
|
+
<span class="spinner-border spinner-border-sm d-none" id="generate-spinner" role="status" aria-hidden="true"></span>
|
112
|
+
Generate Job Description
|
113
|
+
</button>
|
114
|
+
</div>
|
115
|
+
</form>
|
116
|
+
</div>
|
117
|
+
</div>
|
118
|
+
|
119
|
+
<div class="card mb-4" id="result-card" style="display: none;">
|
120
|
+
<div class="card-header bg-success text-white d-flex justify-content-between align-items-center">
|
121
|
+
<span>Generated Job Description</span>
|
122
|
+
<div>
|
123
|
+
<button class="btn btn-sm btn-light" id="copy-jd-btn">Copy</button>
|
124
|
+
<button class="btn btn-sm btn-light ms-2" id="save-jd-btn">Save</button>
|
125
|
+
</div>
|
126
|
+
</div>
|
127
|
+
<div class="card-body">
|
128
|
+
<div id="jd-sections">
|
129
|
+
<h3 id="jd-title" class="mb-3"></h3>
|
130
|
+
<div id="jd-content"></div>
|
131
|
+
</div>
|
132
|
+
</div>
|
133
|
+
</div>
|
134
|
+
</div>
|
135
|
+
|
136
|
+
<script>
|
137
|
+
document.addEventListener('DOMContentLoaded', function() {
|
138
|
+
// Required skills management
|
139
|
+
let requiredSkills = [];
|
140
|
+
let preferredSkills = [];
|
141
|
+
|
142
|
+
// Function to add skill badges
|
143
|
+
function addSkillBadge(skill, isPreferred = false) {
|
144
|
+
const skillArray = isPreferred ? preferredSkills : requiredSkills;
|
145
|
+
const containerId = isPreferred ? 'preferred_skills_container' : 'required_skills_container';
|
146
|
+
const hiddenInputId = isPreferred ? 'preferred_skills' : 'required_skills';
|
147
|
+
|
148
|
+
if (skill && !skillArray.includes(skill)) {
|
149
|
+
skillArray.push(skill);
|
150
|
+
|
151
|
+
const container = document.getElementById(containerId);
|
152
|
+
const badge = document.createElement('span');
|
153
|
+
badge.className = 'badge bg-primary me-1 mb-1 skill-badge';
|
154
|
+
badge.innerHTML = `${skill} <i class="bi bi-x-circle" style="cursor: pointer;"></i>`;
|
155
|
+
container.appendChild(badge);
|
156
|
+
|
157
|
+
// Add remove functionality
|
158
|
+
badge.querySelector('i').addEventListener('click', function() {
|
159
|
+
const index = skillArray.indexOf(skill);
|
160
|
+
if (index > -1) {
|
161
|
+
skillArray.splice(index, 1);
|
162
|
+
badge.remove();
|
163
|
+
document.getElementById(hiddenInputId).value = JSON.stringify(skillArray);
|
164
|
+
}
|
165
|
+
});
|
166
|
+
|
167
|
+
document.getElementById(hiddenInputId).value = JSON.stringify(skillArray);
|
168
|
+
}
|
169
|
+
}
|
170
|
+
|
171
|
+
// Add required skill on button click or Enter press
|
172
|
+
document.getElementById('add-skill-btn').addEventListener('click', function() {
|
173
|
+
const input = document.getElementById('skill-input');
|
174
|
+
addSkillBadge(input.value.trim());
|
175
|
+
input.value = '';
|
176
|
+
});
|
177
|
+
|
178
|
+
document.getElementById('skill-input').addEventListener('keypress', function(e) {
|
179
|
+
if (e.key === 'Enter') {
|
180
|
+
e.preventDefault();
|
181
|
+
addSkillBadge(this.value.trim());
|
182
|
+
this.value = '';
|
183
|
+
}
|
184
|
+
});
|
185
|
+
|
186
|
+
// Add preferred skill on button click or Enter press
|
187
|
+
document.getElementById('add-preferred-skill-btn').addEventListener('click', function() {
|
188
|
+
const input = document.getElementById('preferred-skill-input');
|
189
|
+
addSkillBadge(input.value.trim(), true);
|
190
|
+
input.value = '';
|
191
|
+
});
|
192
|
+
|
193
|
+
document.getElementById('preferred-skill-input').addEventListener('keypress', function(e) {
|
194
|
+
if (e.key === 'Enter') {
|
195
|
+
e.preventDefault();
|
196
|
+
addSkillBadge(this.value.trim(), true);
|
197
|
+
this.value = '';
|
198
|
+
}
|
199
|
+
});
|
200
|
+
|
201
|
+
// Handle form submission
|
202
|
+
document.getElementById('job-description-form').addEventListener('submit', function(e) {
|
203
|
+
e.preventDefault();
|
204
|
+
|
205
|
+
// Validate required skills
|
206
|
+
if (requiredSkills.length === 0) {
|
207
|
+
alert('Please add at least one required skill');
|
208
|
+
return;
|
209
|
+
}
|
210
|
+
|
211
|
+
// Show spinner and disable button
|
212
|
+
const generateBtn = document.getElementById('generate-btn');
|
213
|
+
const spinner = document.getElementById('generate-spinner');
|
214
|
+
generateBtn.disabled = true;
|
215
|
+
spinner.classList.remove('d-none');
|
216
|
+
|
217
|
+
// Collect form data
|
218
|
+
const formData = new FormData(this);
|
219
|
+
const jobData = {};
|
220
|
+
|
221
|
+
for (const [key, value] of formData.entries()) {
|
222
|
+
if (key === 'required_skills' || key === 'preferred_skills') {
|
223
|
+
jobData[key] = JSON.parse(value);
|
224
|
+
} else {
|
225
|
+
jobData[key] = value;
|
226
|
+
}
|
227
|
+
}
|
228
|
+
|
229
|
+
// Send data to backend
|
230
|
+
fetch('/api/generate-jd', {
|
231
|
+
method: 'POST',
|
232
|
+
headers: {
|
233
|
+
'Content-Type': 'application/json'
|
234
|
+
},
|
235
|
+
body: JSON.stringify(jobData)
|
236
|
+
})
|
237
|
+
.then(response => response.json())
|
238
|
+
.then(data => {
|
239
|
+
if (data.success) {
|
240
|
+
// Display the generated JD
|
241
|
+
document.getElementById('jd-title').textContent = jobData.position;
|
242
|
+
document.getElementById('jd-content').innerHTML = data.job_description_text.replace(/\n/g, '<br>');
|
243
|
+
document.getElementById('result-card').style.display = 'block';
|
244
|
+
|
245
|
+
// Scroll to result
|
246
|
+
document.getElementById('result-card').scrollIntoView({ behavior: 'smooth' });
|
247
|
+
} else {
|
248
|
+
alert('Error generating job description: ' + data.error);
|
249
|
+
}
|
250
|
+
})
|
251
|
+
.catch(error => {
|
252
|
+
console.error('Error:', error);
|
253
|
+
alert('Error generating job description. Please try again.');
|
254
|
+
})
|
255
|
+
.finally(() => {
|
256
|
+
// Hide spinner and enable button
|
257
|
+
generateBtn.disabled = false;
|
258
|
+
spinner.classList.add('d-none');
|
259
|
+
});
|
260
|
+
});
|
261
|
+
|
262
|
+
// Copy JD to clipboard
|
263
|
+
document.getElementById('copy-jd-btn').addEventListener('click', function() {
|
264
|
+
const jdText = document.getElementById('jd-content').innerText;
|
265
|
+
const title = document.getElementById('jd-title').innerText;
|
266
|
+
|
267
|
+
navigator.clipboard.writeText(title + '\n\n' + jdText)
|
268
|
+
.then(() => {
|
269
|
+
const originalText = this.textContent;
|
270
|
+
this.textContent = 'Copied!';
|
271
|
+
setTimeout(() => {
|
272
|
+
this.textContent = originalText;
|
273
|
+
}, 2000);
|
274
|
+
})
|
275
|
+
.catch(err => {
|
276
|
+
console.error('Failed to copy: ', err);
|
277
|
+
alert('Failed to copy to clipboard');
|
278
|
+
});
|
279
|
+
});
|
280
|
+
|
281
|
+
// Save JD
|
282
|
+
document.getElementById('save-jd-btn').addEventListener('click', function() {
|
283
|
+
const title = document.getElementById('jd-title').innerText;
|
284
|
+
const jdText = document.getElementById('jd-content').innerText;
|
285
|
+
|
286
|
+
fetch('/api/save-jd', {
|
287
|
+
method: 'POST',
|
288
|
+
headers: {
|
289
|
+
'Content-Type': 'application/json'
|
290
|
+
},
|
291
|
+
body: JSON.stringify({
|
292
|
+
title: title,
|
293
|
+
content: jdText,
|
294
|
+
metadata: {
|
295
|
+
position: document.getElementById('position').value,
|
296
|
+
location: document.getElementById('location').value,
|
297
|
+
required_skills: requiredSkills,
|
298
|
+
preferred_skills: preferredSkills
|
299
|
+
}
|
300
|
+
})
|
301
|
+
})
|
302
|
+
.then(response => response.json())
|
303
|
+
.then(data => {
|
304
|
+
if (data.success) {
|
305
|
+
alert('Job description saved successfully!');
|
306
|
+
} else {
|
307
|
+
alert('Error saving job description: ' + data.error);
|
308
|
+
}
|
309
|
+
})
|
310
|
+
.catch(error => {
|
311
|
+
console.error('Error:', error);
|
312
|
+
alert('Error saving job description');
|
313
|
+
});
|
314
|
+
});
|
315
|
+
});
|
316
|
+
</script>
|
317
|
+
</body>
|
318
|
+
</html>
|