File size: 7,004 Bytes
504df0f
 
3c4bd31
504df0f
 
 
 
 
3c4bd31
 
504df0f
 
 
 
 
 
 
af02e64
504df0f
 
3c4bd31
504df0f
af02e64
d8acd61
4741385
 
 
 
 
504df0f
4741385
 
 
 
 
 
 
 
 
 
 
 
27994de
4741385
 
 
 
 
 
ce04e48
4741385
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d8acd61
4741385
 
4991915
4741385
 
4991915
4741385
 
 
 
2ae57cb
504df0f
 
 
 
2ae57cb
 
 
4741385
2ae57cb
4741385
2ae57cb
 
 
 
4741385
2ae57cb
4741385
 
4991915
 
4741385
2ae57cb
 
 
4741385
 
 
2ae57cb
 
4741385
 
 
2ae57cb
 
4741385
 
2ae57cb
5c64dc0
4741385
5c64dc0
4741385
5c64dc0
4741385
 
5c64dc0
4741385
 
5c64dc0
 
4741385
5c64dc0
2ae57cb
5c64dc0
4741385
 
 
5c64dc0
 
4741385
 
 
 
 
 
 
4991915
4741385
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5c64dc0
 
4741385
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
{% 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 %}