Spaces:
Paused
Paused
| {% extends "base.html" %} | |
| {% block title %}Apply for {{ job.role }} - Codingo{% endblock %} | |
| {% block hero %} | |
| <section class="hero" style="padding: 3rem 1rem;"> | |
| <div class="container"> | |
| <div class="hero-content"> | |
| <h1>Apply for {{ job.role }}</h1> | |
| <p>{{ job.company }}{% if job.seniority %} • {{ job.seniority }}{% endif %}</p> | |
| </div> | |
| </div> | |
| </section> | |
| {% endblock %} | |
| {% block content %} | |
| <section class="content-section"> | |
| <!-- <ul class="breadcrumbs"> | |
| <li><a href="{{ url_for('index') }}">Home</a></li> | |
| <li><a href="{{ url_for('jobs') }}">Jobs</a></li> | |
| <li><a href="{{ url_for('job_detail', job_id=job.id) }}">{{ job.role }}</a></li> | |
| <li>Apply</li> | |
| </ul> --> | |
| <div class="card"> | |
| <div class="card-header"> | |
| <h2>Submit Your Application</h2> | |
| <p>Please upload your resume (PDF, DOCX). Your file will be saved securely for recruiters to review.</p> | |
| </div> | |
| <div class="card-body"> | |
| <!-- Application Form --> | |
| <form method="POST" enctype="multipart/form-data"> | |
| <div class="form-group"> | |
| <label for="resume">Upload Resume</label> | |
| <!-- Resume upload remains mandatory. The file will be stored for recruiter review but is no longer parsed automatically. --> | |
| <input type="file" name="resume" id="resume" class="form-control" required accept=".pdf,.doc,.docx"> | |
| <!-- Parse Resume button sits beside the upload to allow users to extract information from their CV. It uses a | |
| type="button" so that clicking it does not submit the form. The actual parsing logic is defined in | |
| the script at the bottom of this template. --> | |
| <button type="button" id="parse-resume" class="btn btn-secondary" style="margin-top:0.5rem;">Parse Resume</button> | |
| </div> | |
| <!-- Name field added to capture the applicant's full name. This input can be autofilled by the resume parser, | |
| but remains editable so applicants can correct any mistakes. --> | |
| <div class="form-group"> | |
| <label for="full-name">Full Name</label> | |
| <input type="text" name="full_name" id="full-name" class="form-control" placeholder="e.g. Jane Doe" required> | |
| </div> | |
| <!-- | |
| Collect the candidate's skills, experience and education manually. | |
| These fields allow applicants to highlight their background even when resume | |
| parsing is disabled. Entries can be separated by commas, semicolons or newlines; | |
| the backend will normalise them into lists. | |
| --> | |
| <div class="form-group"> | |
| <label for="skills">Skills</label> | |
| <textarea name="skills" id="skills" class="form-control" rows="3" placeholder="e.g. Python, Data Analysis, Project Management" required></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label for="experience">Experience</label> | |
| <textarea name="experience" id="experience" class="form-control" rows="3" placeholder="e.g. 3 years at TechCorp as a Backend Developer" required></textarea> | |
| </div> | |
| <div class="form-group"> | |
| <label for="education">Education</label> | |
| <textarea name="education" id="education" class="form-control" rows="3" placeholder="e.g. B.Sc. in Computer Science, M.Sc. in Data Science" required></textarea> | |
| </div> | |
| <!-- Interview guidelines removed from this page. They are now displayed on the | |
| "My Applications" page so applicants see them before taking the interview. --> | |
| <div class="application-actions" style="margin-top: 2rem;"> | |
| <button type="submit" class="btn btn-primary">Submit Application</button> | |
| </div> | |
| </form> | |
| <div style="margin-top: 1.5rem; text-align: center;"> | |
| <a href="{{ url_for('job_detail', job_id=job.id) }}" class="btn btn-outline">Back to Job Details</a> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <style> | |
| .form-group label { | |
| font-weight: 600; | |
| color: var(--primary); | |
| margin-bottom: 0.5rem; | |
| display: block; | |
| } | |
| .form-control { | |
| width: 100%; | |
| padding: 0.75rem; | |
| font-size: 1rem; | |
| border-radius: 6px; | |
| border: 1px solid #ccc; | |
| } | |
| .application-actions { | |
| text-align: center; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, var(--primary), var(--secondary)); | |
| color: white; | |
| padding: 0.75rem 1.5rem; | |
| font-weight: 500; | |
| border: none; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| } | |
| .btn-primary:hover { | |
| opacity: 0.9; | |
| } | |
| /* Secondary button styling used for the "Parse Resume" control */ | |
| .btn-secondary { | |
| background: var(--secondary); | |
| color: white; | |
| padding: 0.75rem 1.5rem; | |
| font-weight: 500; | |
| border: none; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| } | |
| .btn-secondary:hover { | |
| opacity: 0.9; | |
| } | |
| </style> | |
| {# Resume parsing script: attaches click handler to the Parse Resume button. It performs an asynchronous | |
| POST to a placeholder endpoint (`/parse_resume`) with the uploaded file and, upon success, | |
| populates the corresponding form fields. Users can still edit the populated fields. #} | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| const parseBtn = document.getElementById('parse-resume'); | |
| if (!parseBtn) return; | |
| parseBtn.addEventListener('click', function() { | |
| const resumeInput = document.getElementById('resume'); | |
| if (!resumeInput || !resumeInput.files || resumeInput.files.length === 0) { | |
| alert('Please upload your resume before parsing.'); | |
| return; | |
| } | |
| const formData = new FormData(); | |
| formData.append('resume', resumeInput.files[0]); | |
| fetch('/parse_resume', { | |
| method: 'POST', | |
| body: formData | |
| }).then(resp => resp.json()) | |
| .then(data => { | |
| if (data) { | |
| if (data.name && document.getElementById('full-name')) { | |
| document.getElementById('full-name').value = data.name; | |
| } | |
| if (data.skills && document.getElementById('skills')) { | |
| document.getElementById('skills').value = data.skills; | |
| } | |
| if (data.education && document.getElementById('education')) { | |
| document.getElementById('education').value = data.education; | |
| } | |
| if (data.experience && document.getElementById('experience')) { | |
| document.getElementById('experience').value = data.experience; | |
| } | |
| } | |
| }) | |
| .catch(err => { | |
| console.error(err); | |
| alert('Unable to parse resume. Please try again later.'); | |
| }); | |
| }); | |
| }); | |
| </script> | |
| {% endblock %} | |