1090 lines
56 KiB
XML
1090 lines
56 KiB
XML
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<odoo>
|
|
<!-- Job List Partial -->
|
|
<template id="job_list_partial_page">
|
|
<div class="ats-list-container">
|
|
<!-- Search -->
|
|
<!-- Dynamic Job Stats Header -->
|
|
<!-- Job List and Details -->
|
|
<div class="ats-list-body">
|
|
<!-- Job List Panel -->
|
|
<div class="ats-list-left expanded" id="job-list-panel">
|
|
<div class="ats-list-search">
|
|
<input type="text" id="ats-search" placeholder="🔍 Search Jobs..."/>
|
|
</div>
|
|
|
|
<!-- Toggle Button -->
|
|
|
|
<button id="job-list-sidebar-toggle-btn" class="ats-list-toggle-btn">☰</button>
|
|
|
|
<!-- Action Buttons Header -->
|
|
<div class="ats-actions-header">
|
|
<button class="btn" type="button" id="activeRecords" style="left:0;">
|
|
Active Records (
|
|
<span id="active-records-count">
|
|
<t t-esc="len(jobs)"/>
|
|
</span>
|
|
)
|
|
</button>
|
|
<button class="btn add-create-btn" type="button" id="add-jd-create-btn" style="right:0;">
|
|
<span class="plus-icon">+</span>
|
|
Add New JD
|
|
</button>
|
|
</div>
|
|
<div class="job-stats-header" id="job-stats-header">
|
|
<div class="job-stats-values">
|
|
<span class="badge badge-primary stat-toggle active" data-type="recruit">To Submit
|
|
</span>
|
|
<span class="badge badge-warning stat-toggle active" data-type="submitted">Submitted
|
|
</span>
|
|
<span class="badge badge-success stat-toggle active" data-type="hired">Hired</span>
|
|
<span class="badge badge-danger stat-toggle active" data-type="rejected">Rejected</span>
|
|
</div>
|
|
</div>
|
|
|
|
<ul class="ats-list">
|
|
<t t-foreach="jobs" t-as="job">
|
|
<li class="job-item ats-item"
|
|
t-att-data-id="job.id"
|
|
t-att-data-recruit="str(job.no_of_eligible_submissions or 0)"
|
|
t-att-data-submitted="str(job.no_of_submissions or 0)"
|
|
t-att-data-hired="str(job.no_of_hired_employee or 0)"
|
|
t-att-data-rejected="str(job.no_of_refused_submissions or 0)">
|
|
|
|
<div class="ats-title">
|
|
<strong>
|
|
<t t-if="job.display_name">
|
|
<t t-esc="job.display_name"/>
|
|
</t>
|
|
<t t-else="">
|
|
<t t-esc="job.job_id.display_name"/>
|
|
</t>
|
|
</strong>
|
|
</div>
|
|
|
|
<div class="ats-meta">
|
|
<span>
|
|
<t t-esc="job.job_category.display_name"/>
|
|
</span>
|
|
</div>
|
|
|
|
<div class="ats-badges">
|
|
<span class="badge badge-primary job-badge" data-type="recruit">
|
|
<t t-esc="job.no_of_eligible_submissions or 0"/>
|
|
</span>
|
|
<span class="badge badge-warning job-badge" data-type="submitted">
|
|
<t t-esc="job.no_of_submissions or 0"/>
|
|
</span>
|
|
<span class="badge badge-success job-badge" data-type="hired">
|
|
<t t-esc="job.no_of_hired_employee or 0"/>
|
|
</span>
|
|
<span class="badge badge-danger job-badge" data-type="rejected">
|
|
<t t-esc="job.no_of_refused_submissions or 0"/>
|
|
</span>
|
|
</div>
|
|
</li>
|
|
</t>
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
<!-- Job Detail Panel -->
|
|
<div id="job-detail" class="ats-detail">
|
|
<em>Select a job to view details.</em>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<t t-call="hr_recruitment_web_app.add_jd_modal_template"/>
|
|
<t t-call="hr_recruitment_web_app.matching_candidates_popup"/>
|
|
<t t-call="hr_recruitment_web_app.applicants_popup"/>
|
|
|
|
</template>
|
|
|
|
|
|
<template id="add_jd_modal_template">
|
|
<div id="add-jd-modal" class="new-jd-container jd-modal">
|
|
<div class="jd-modal-content">
|
|
<div class="jd-modal-header">
|
|
<div class="header-content">
|
|
<i class="fas fa-file-alt header-icon"></i>
|
|
<h3>Create New Job Description</h3>
|
|
</div>
|
|
<span class="jd-modal-close">&times;</span>
|
|
</div>
|
|
|
|
<div class="jd-modal-body">
|
|
<form id="jd-form" class="jd-creation-form">
|
|
<!-- Job Position and ID Section -->
|
|
<div class="form-section profile-section">
|
|
<h4 class="section-title">
|
|
<i class="fas fa-id-card"></i>
|
|
Job Identification
|
|
</h4>
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="job-sequence">Unique ID</label>
|
|
<input type="text" id="job-sequence" class="form-input"
|
|
placeholder="Unique ID" required=""/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="job-position">Job Position</label>
|
|
<select id="job-position" class="form-select select2" required="" data-placeholder="Select Job Position">
|
|
<t t-set="all_jobs" t-value="request.env['hr.job'].search([])"/>
|
|
<option value="">Select Job Position</option>
|
|
<t t-foreach="all_jobs" t-as="job">
|
|
<option t-att-value="job.id">
|
|
<t t-esc="job.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Section 1: Basic Information -->
|
|
<div class="form-section profile-section">
|
|
<h4 class="section-title">
|
|
<i class="fas fa-info-circle"></i>
|
|
Basic Information
|
|
</h4>
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="job-category">Category</label>
|
|
<select id="job-category" class="form-select" required="">
|
|
<t t-set="categories" t-value="request.env['job.category'].search([])"/>
|
|
<t t-foreach="categories" t-as="category">
|
|
<option t-att-value="category.id">
|
|
<t t-esc="category.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="job-priority">Priority</label>
|
|
<select id="job-priority" class="form-select" required="">
|
|
<option value="low">Low</option>
|
|
<option value="medium">Medium</option>
|
|
<option value="high">High</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label>Work Type</label>
|
|
<div class="radio-group">
|
|
<div class="radio-option">
|
|
<input class="form-check-input" type="radio" name="work-type"
|
|
id="work-type-internal" value="internal" checked=""/>
|
|
<label class="form-check-label" for="work-type-internal">
|
|
In-House
|
|
</label>
|
|
</div>
|
|
<div class="radio-option">
|
|
<input class="form-check-input" type="radio" name="work-type"
|
|
id="work-type-external" value="external"/>
|
|
<label class="form-check-label" for="work-type-external">
|
|
Client-Side
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="client-session">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="client-company">Client Company</label>
|
|
<select id="client-company" class="form-select select2" required=""
|
|
data-placeholder="Select Client Company">
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="client-id">Client</label>
|
|
<select id="client-id" class="form-select select2" required=""
|
|
data-placeholder="Select Client">
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Section 2: Employment Details -->
|
|
<div class="form-section personal-section">
|
|
<h4 class="section-title">
|
|
<i class="fas fa-file-signature"></i>
|
|
Employment Details
|
|
</h4>
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="employment-type">Employment Type</label>
|
|
<select id="employment-type" class="form-select" required="">
|
|
<t t-set="emp_types" t-value="request.env['hr.contract.type'].search([])"/>
|
|
<t t-foreach="emp_types" t-as="emp_type">
|
|
<option t-att-value="emp_type.id">
|
|
<t t-esc="emp_type.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="job-budget">Budget</label>
|
|
<input id="job-budget" class="form-input" placeholder="Enter budget"/>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="job-no-of-positions">Positions</label>
|
|
<input id="job-no-of-positions" class="form-input" type="number" min="0"
|
|
placeholder="Number of positions" required=""/>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="job-eligible-submissions">Eligible Submissions</label>
|
|
<input id="job-eligible-submissions" class="form-input" type="number" min="0"
|
|
placeholder="Eligible Submissions" required=""/>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="target-from">Target From</label>
|
|
<input type="date" id="target-from" class="form-input"/>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="target-to">Target To</label>
|
|
<input type="date" id="target-to" class="form-input"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Section 3: Skills & Requirements -->
|
|
<div class="form-section professional-section">
|
|
<h4 class="section-title">
|
|
<i class="fas fa-tasks"></i>
|
|
Skills & Requirements
|
|
</h4>
|
|
<t t-set="skills" t-value="request.env['hr.skill'].search([])"/>
|
|
|
|
<div class="form-grid" id="skill_requirements">
|
|
<div class="form-group">
|
|
<label for="job-primary-skills">Primary Skills</label>
|
|
<select id="job-primary-skills" class="form-select select2" multiple="multiple"
|
|
data-placeholder="Select primary skills" draggable="true">
|
|
<t t-foreach="skills" t-as="skill" t-key="skill.id">
|
|
<option t-att-value="skill.id">
|
|
<t t-esc="skill.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="job-secondary-skills">Secondary Skills</label>
|
|
<select id="job-secondary-skills" class="form-select select2" multiple="multiple"
|
|
data-placeholder="Select secondary skills" draggable="true">
|
|
<t t-foreach="skills" t-as="skill" t-key="skill.id">
|
|
<option t-att-value="skill.id">
|
|
<t t-esc="skill.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="job-experience">Experience</label>
|
|
<select id="job-experience" class="form-select">
|
|
<t t-set="job_experience" t-value="request.env['candidate.experience'].search([])"/>
|
|
<t t-foreach="job_experience" t-as="experience">
|
|
<option t-att-value="experience.id">
|
|
<t t-esc="experience.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Section 4: Recruitment Team -->
|
|
<div class="form-section address-section">
|
|
<h4 class="section-title">
|
|
<i class="fas fa-users-cog"></i>
|
|
Recruitment Team
|
|
</h4>
|
|
<div class="recruitment-team">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="primary-recruiter">Primary Recruiter</label>
|
|
<select id="primary-recruiter" class="form-select select2"
|
|
data-placeholder="Choose recruiter">
|
|
<t t-set="user_ids"
|
|
t-value="request.env['res.users'].sudo().search([])"/>
|
|
<t t-foreach="user_ids" t-as="user">
|
|
<option t-att-value="user.id"
|
|
t-att-data-image="'/web/image/res.users/' + str(user.id) + '/image_128'">
|
|
<t t-esc="user.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="secondary-recruiter">Secondary Recruiter</label>
|
|
<select id="secondary-recruiter" class="form-select select2"
|
|
multiple="multiple" data-placeholder="Select recruiters">
|
|
<t t-set="user_ids" t-value="request.env['res.users'].sudo().search([])"/>
|
|
<t t-foreach="user_ids" t-as="user">
|
|
<option t-att-value="user.id"
|
|
t-att-data-image="'/web/image/res.users/' + str(user.id) + '/image_128'">
|
|
<t t-esc="user.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Section 5: Additional Information -->
|
|
<div class="form-section jd-section">
|
|
<h4 class="section-title">
|
|
<i class="fas fa-info-circle"></i>
|
|
Additional Information
|
|
</h4>
|
|
<div class="additional-info">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="job-locations">Locations</label>
|
|
<select id="job-locations" class="form-select select2" multiple="multiple"
|
|
data-placeholder="Select job locations">
|
|
<t t-set="location_ids" t-value="request.env['hr.location'].search([])"/>
|
|
<t t-foreach="location_ids" t-as="location">
|
|
<option t-att-value="location.id">
|
|
<t t-esc="location.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="recruitment-stages">Recruitment Stages</label>
|
|
<select id="recruitment-stages" class="form-select select2" multiple="multiple"
|
|
data-placeholder="Select stages">
|
|
<t t-set="recruitment_stages"
|
|
t-value="request.env['hr.recruitment.stage'].search([])"/>
|
|
<t t-foreach="recruitment_stages" t-as="stage">
|
|
<option t-att-value="stage.id"
|
|
t-att-selected="'selected' if stage.is_default_field else None">
|
|
<t t-esc="stage.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job Description Section -->
|
|
<div class="form-section description-section">
|
|
<h4 class="section-title">
|
|
<i class="fas fa-align-left"></i>
|
|
Job Description
|
|
</h4>
|
|
<div class="form-group full-width">
|
|
<div id="job-description-editor" class="oe_editor"
|
|
style="min-height: 300px; border: 1px solid #ddd; padding: 8px;"></div>
|
|
<textarea id="job-description" name="description" style="display:none;"></textarea>
|
|
</div>
|
|
</div>
|
|
</form> </div>
|
|
|
|
<div class="form-actions">
|
|
<button type="button" class="btn btn-jd-primary" id="upload-jd">
|
|
<i class="fas fa-upload"></i>
|
|
Upload JD
|
|
</button>
|
|
<div class="footer-right">
|
|
<button type="button" class="btn-cancel">
|
|
Cancel
|
|
</button>
|
|
<button type="button" class="btn-jd-primary" id="save-jd">
|
|
<i class="fas fa-save"></i>
|
|
Save Job Description
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Job Detail Partial - Web JD Version -->
|
|
<template id="job_detail_partial">
|
|
<div class="ats-grid job-detail-container" id="ats-details-container" t-att-data-job-id="job.id">
|
|
<!-- Close button -->
|
|
<button type="button" class="close-detail" aria-label="Close">
|
|
<span aria-hidden="true">&times;</span>
|
|
</button>
|
|
<!-- Smart Button -->
|
|
<div class="smart-button-container">
|
|
<button class="btn btn-primary smart-button" data-popup-type="matchingCandidates" data-popup-title="Matching Candidates" id="matching-jd-candidates">
|
|
<i class="fa fa-magic me-1"></i> Matching Candidates
|
|
</button>
|
|
<button class="btn btn-primary smart-button" data-popup-type="applicants" data-popup-title="Applicants" id="jd-applicants">
|
|
<i class="fa fa-magic me-1"></i> Applicants
|
|
</button>
|
|
</div>
|
|
<!-- Published Ribbon -->
|
|
<div t-att-class="'status-ribbon ribbon-' + ('published' if job.website_published else 'not-published')">
|
|
<t t-esc="'Published' if job.website_published else 'Not Published'"/>
|
|
</div>
|
|
<!-- Navigation Sidebar -->
|
|
<div class="ats-card span-3" id="ats-overview">
|
|
<h4 class="section-title">
|
|
<i class="fa fa-list-ul me-2"></i>Quick Nav
|
|
</h4>
|
|
<div class="overview-nav">
|
|
<ul class="nav-list">
|
|
<li>
|
|
<a href="#ats-header" class="nav-link smooth-scroll"><i class="fa fa-info-circle me-2"></i>Job Overview</a>
|
|
</li>
|
|
<li>
|
|
<a href="#ats-basic-info" class="nav-link smooth-scroll"><i class="fa fa-info me-2"></i>Basic Information</a>
|
|
</li>
|
|
<li>
|
|
<a href="#ats-requirements" class="nav-link smooth-scroll"><i class="fa fa-tasks me-2"></i>Requirements</a>
|
|
</li>
|
|
<li>
|
|
<a href="#ats-request-info" class="nav-link smooth-scroll"><i class="fa fa-user-tie me-2"></i>Request Information</a>
|
|
</li>
|
|
<li>
|
|
<a href="#ats-team" class="nav-link smooth-scroll"><i class="fa fa-users me-2"></i>Recruitment Team</a>
|
|
</li>
|
|
<li>
|
|
<a href="#ats-description" class="nav-link smooth-scroll"><i class="fa fa-file-text me-2"></i>Job Description</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job Header Section -->
|
|
<div class="ats-card span-9" id="ats-header">
|
|
<div class="card-header">
|
|
<h2 class="ats-title">
|
|
<span t-esc="job.job_id.display_name"/>
|
|
</h2>
|
|
<button class="edit-card-btn" data-card="header">
|
|
<i class="fa fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
<div class="ats-meta">
|
|
<span class="meta-item">
|
|
<i class="fa fa-briefcase me-1"></i>
|
|
<span t-esc="job.job_category.display_name"/>
|
|
</span>
|
|
<span class="meta-item">
|
|
<i class="fa fa-map-marker me-1"></i>
|
|
<span t-if="job.address_id" t-esc="job.address_id.display_name"/>
|
|
<span t-else="">Remote</span>
|
|
</span>
|
|
<span class="meta-item">
|
|
<i class="fa fa-clock-o me-1"></i>
|
|
<t t-if="job.recruitment_type == 'internal'">In-House</t>
|
|
<t t-elif="job.recruitment_type == 'external'">Client-Side</t>
|
|
<t t-else="">Unknown</t>
|
|
</span>
|
|
<span class="meta-item">
|
|
<i class="fa fa-star me-1"></i>
|
|
<span t-esc="job.job_priority"/>
|
|
</span>
|
|
</div>
|
|
<!-- Status Bar -->
|
|
<div class="status-bar">
|
|
<div class="recruitment-status">
|
|
<span class="status-label">Recruitment Status:</span>
|
|
<span class="status-value" t-esc="job.recruitment_status"/>
|
|
</div>
|
|
<div class="recruitment-progress">
|
|
<div class="progress">
|
|
<div class="progress-bar" role="progressbar"
|
|
t-att-style="'width: ' + str(int(progress)) + '%;'"
|
|
t-att-aria-valuenow="progress"
|
|
aria-valuemin="0" aria-valuemax="100">
|
|
<t t-esc="str(int(progress)) + '%'"/>
|
|
</div>
|
|
</div>
|
|
<br/>
|
|
<br/>
|
|
<small class="text-muted">
|
|
<t t-esc="job.no_of_hired_employee or 0"/>
|
|
of
|
|
<t t-esc="job.no_of_recruitment or 0"/>
|
|
positions filled
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Basic Information Section -->
|
|
<div class="ats-card span-6" id="ats-basic-info">
|
|
<div class="card-header">
|
|
<h4 class="section-title">
|
|
<i class="fa fa-info-circle me-2"></i>Basic Information
|
|
</h4>
|
|
<button class="edit-card-btn" data-card="basic-info"
|
|
t-att-data-employment-type="job.contract_type_id.id or ''"
|
|
t-att-data-budget="job.budget or ''"
|
|
t-att-data-target-from="job.target_from or ''"
|
|
t-att-data-target-to="job.target_to or ''"
|
|
t-att-data-no-of-positions="job.no_of_recruitment or 0"
|
|
t-att-data-eligible-submissions="job.no_of_eligible_submissions or 0">
|
|
<i class="fa fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
<div class="detail-grid">
|
|
<div class="detail-row">
|
|
<span class="detail-label">Employment Type:</span>
|
|
<span class="detail-value" t-esc="job.contract_type_id.display_name or 'N/A'"/>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">Budget:</span>
|
|
<span class="detail-value" t-esc="job.budget or 'Not specified'"/>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">Target From:</span>
|
|
<span class="detail-value" t-esc="job.target_from or 'N/A'"/>
|
|
<t t-if="job.target_to">
|
|
-
|
|
<span class="detail-value" t-esc="job.target_to or 'N/A'"/>
|
|
</t>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">Number of Positions:</span>
|
|
<span class="detail-value" t-esc="job.no_of_recruitment or 'Not specified'"/>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">Eligible Submissions:</span>
|
|
<span class="detail-value" t-esc="job.no_of_eligible_submissions or 'Not specified'"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Requirements Section -->
|
|
<div class="ats-card span-6" id="ats-requirements">
|
|
<div class="card-header">
|
|
<h4 class="section-title">
|
|
<i class="fa fa-tasks me-2"></i>Requirements
|
|
</h4>
|
|
<button class="edit-card-btn" data-card="requirements"
|
|
t-att-data-experience="job.experience.id or ''"
|
|
t-att-data-primary-skills="','.join(str(skill.id) for skill in job.skill_ids)"
|
|
t-att-data-secondary-skills="','.join(str(skill.id) for skill in job.secondary_skill_ids)"
|
|
t-att-data-locations="','.join(str(location.id) for location in job.locations)">
|
|
<i class="fa fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
<div class="detail-grid">
|
|
<div class="detail-row">
|
|
<span class="detail-label">Experience:</span>
|
|
<span class="detail-value" t-esc="job.experience.display_name or 'Not specified'"/>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">Primary Skills:</span>
|
|
<div class="skills-list">
|
|
<t t-foreach="job.skill_ids" t-as="skill">
|
|
<span class="skill-badge" t-esc="skill.display_name"/>
|
|
</t>
|
|
<t t-if="not job.skill_ids">Not specified</t>
|
|
</div>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">Secondary Skills:</span>
|
|
<div class="skills-list">
|
|
<t t-foreach="job.secondary_skill_ids" t-as="skill">
|
|
<span class="skill-badge" t-esc="skill.display_name"/>
|
|
</t>
|
|
<t t-if="not job.secondary_skill_ids">Not specified</t>
|
|
</div>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">Locations:</span>
|
|
<div class="skills-list">
|
|
<t t-foreach="job.locations" t-as="location">
|
|
<span class="location-badge" t-esc="location.display_name"/>
|
|
</t>
|
|
<t t-if="not job.locations">Not specified</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Request Information Section -->
|
|
<div class="ats-card span-6" id="ats-request-info">
|
|
<div class="card-header">
|
|
<h4 class="section-title">
|
|
<i class="fa fa-user-tie me-2"></i>Request Information
|
|
</h4>
|
|
<button class="edit-card-btn" data-card="request-info"
|
|
t-att-data-requested-by="job.requested_by.id or ''"
|
|
t-att-data-department="job.department_id.id or ''"
|
|
t-att-data-company="job.address_id.id or ''">
|
|
<i class="fa fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
<div class="detail-grid">
|
|
<div class="detail-row">
|
|
<span class="detail-label">Requested By:</span>
|
|
<span class="detail-value" t-esc="job.requested_by.display_name or 'N/A'"/>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">Department:</span>
|
|
<span class="detail-value" t-esc="job.department_id.display_name or 'N/A'"/>
|
|
</div>
|
|
<div class="detail-row">
|
|
<span class="detail-label">Company:</span>
|
|
<span class="detail-value" t-esc="job.address_id.display_name or 'N/A'"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recruitment Team Section -->
|
|
<div class="ats-card span-6" id="ats-team">
|
|
<div class="card-header">
|
|
<h4 class="section-title">
|
|
<i class="fa fa-users me-2"></i>Recruitment Team
|
|
</h4>
|
|
<button class="edit-card-btn" data-card="team"
|
|
t-att-data-primary-recruiter="job.user_id.id or ''"
|
|
t-att-data-secondary-recruiters="','.join(str(user.id) for user in job.interviewer_ids)">
|
|
<i class="fa fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
<div class="team-member">
|
|
<div class="member-header">
|
|
<i class="fa fa-user-circle me-2"></i>
|
|
<span class="member-title">Primary Recruiter</span>
|
|
</div>
|
|
<div class="member-details">
|
|
<t t-if="job.user_id">
|
|
<div class="recruiter-inline d-flex align-items-center">
|
|
<img t-att-src="'/web/image/res.users/' + str(job.user_id.id) + '/image_128'"
|
|
class="rounded-circle recruiter-photo me-3"
|
|
alt="Recruiter Photo"/>
|
|
<div>
|
|
<div class="member-name" t-esc="job.user_id.display_name"/>
|
|
<div class="member-email">
|
|
<i class="fa fa-envelope me-1"></i>
|
|
<span t-esc="job.user_id.email"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
<t t-else="">
|
|
<div class="alert alert-warning">No primary recruiter assigned</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
<div class="team-members">
|
|
<div class="member-header mb-3">
|
|
<i class="fa fa-users me-2"></i>
|
|
<span class="member-title">Secondary Recruiters</span>
|
|
</div>
|
|
<t t-if="job.interviewer_ids">
|
|
<div class="row row-cols-1 row-cols-md-2 g-4">
|
|
<t t-foreach="job.interviewer_ids" t-as="interviewer">
|
|
<div class="col">
|
|
<div class="team-member recruiter-inline">
|
|
<img t-att-src="'/web/image/res.users/' + str(interviewer.id) + '/image_128'"
|
|
class="rounded-circle recruiter-photo"
|
|
alt="Recruiter Photo"/>
|
|
<div>
|
|
<div class="member-name" t-esc="interviewer.display_name"/>
|
|
<div class="member-email">
|
|
<i class="fa fa-envelope me-1"></i>
|
|
<span t-esc="interviewer.email"/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
<t t-else="">
|
|
<div class="alert alert-info">No secondary recruiters assigned</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Job Description Section -->
|
|
<div class="ats-card span-12" id="ats-description">
|
|
<div class="card-header">
|
|
<h4 class="section-title">
|
|
<i class="fa fa-file-text me-2"></i>Job Description
|
|
</h4>
|
|
<button class="edit-card-btn" data-card="description"
|
|
t-att-data-description="job.description or ''">
|
|
<i class="fa fa-edit"></i>
|
|
</button>
|
|
</div>
|
|
<div class="description-content">
|
|
<t t-if="job.description">
|
|
<t t-raw="job.description"/>
|
|
</t>
|
|
<t t-else="">
|
|
<div class="alert alert-info">No description provided for this job.</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Form Templates -->
|
|
<template id="edit_form_template">
|
|
<div class="edit-form-container">
|
|
<div class="form-header">
|
|
<h4 class="section-title">Edit <t t-esc="title"/></h4>
|
|
<div class="form-actions">
|
|
<button class="btn-icon save-edit-btn" title="Save">
|
|
<i class="fas fa-check"></i>
|
|
</button>
|
|
<button class="btn-icon cancel-edit-btn" title="Cancel">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="form-content">
|
|
<t t-raw="form_content"/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Basic Information Edit Form -->
|
|
<template id="basic_info_edit_form">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="edit-employment-type">Employment Type:</label>
|
|
<select id="edit-employment-type" class="form-select">
|
|
<t t-set="emp_types" t-value="request.env['hr.contract.type'].search([])"/>
|
|
<t t-foreach="emp_types" t-as="emp_type">
|
|
<option t-att-value="emp_type.id">
|
|
<t t-esc="emp_type.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-budget">Budget:</label>
|
|
<input type="text" id="edit-budget" class="form-input"/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-target-from">Target From:</label>
|
|
<input type="date" id="edit-target-from" class="form-input"/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-target-to">Target To:</label>
|
|
<input type="date" id="edit-target-to" class="form-input"/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-no-of-positions">Number of Positions:</label>
|
|
<input type="number" id="edit-no-of-positions" class="form-input"/>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-eligible-submissions">Eligible Submissions:</label>
|
|
<input type="number" id="edit-eligible-submissions" class="form-input"/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Requirements Edit Form -->
|
|
<template id="requirements_edit_form">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="edit-experience">Experience:</label>
|
|
<select id="edit-experience" class="form-select">
|
|
<t t-set="job_experience" t-value="request.env['candidate.experience'].search([])"/>
|
|
<t t-foreach="job_experience" t-as="experience">
|
|
<option t-att-value="experience.id">
|
|
<t t-esc="experience.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-primary-skills">Primary Skills:</label>
|
|
<select id="edit-primary-skills" class="form-select" multiple="multiple">
|
|
<t t-set="skills" t-value="request.env['hr.skill'].search([])"/>
|
|
<t t-foreach="skills" t-as="skill">
|
|
<option t-att-value="skill.id">
|
|
<t t-esc="skill.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-secondary-skills">Secondary Skills:</label>
|
|
<select id="edit-secondary-skills" class="form-select" multiple="multiple">
|
|
<t t-set="skills" t-value="request.env['hr.skill'].search([])"/>
|
|
<t t-foreach="skills" t-as="skill">
|
|
<option t-att-value="skill.id">
|
|
<t t-esc="skill.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-locations">Locations:</label>
|
|
<select id="edit-locations" class="form-select" multiple="multiple">
|
|
<t t-set="location_ids" t-value="request.env['hr.location'].search([])"/>
|
|
<t t-foreach="location_ids" t-as="location">
|
|
<option t-att-value="location.id">
|
|
<t t-esc="location.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Request Information Edit Form -->
|
|
<template id="request_info_edit_form">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="edit-requested-by">Requested By:</label>
|
|
<select id="edit-requested-by" class="form-select">
|
|
<t t-set="user_ids" t-value="request.env['res.users'].sudo().search([])"/>
|
|
<t t-foreach="user_ids" t-as="user">
|
|
<option t-att-value="user.id">
|
|
<t t-esc="user.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-department">Department:</label>
|
|
<select id="edit-department" class="form-select">
|
|
<t t-set="departments" t-value="request.env['hr.department'].search([])"/>
|
|
<t t-foreach="departments" t-as="department">
|
|
<option t-att-value="department.id">
|
|
<t t-esc="department.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-company">Company:</label>
|
|
<select id="edit-company" class="form-select">
|
|
<t t-set="companies" t-value="request.env['res.partner'].search([('is_company', '=', True)])"/>
|
|
<t t-foreach="companies" t-as="company">
|
|
<option t-att-value="company.id">
|
|
<t t-esc="company.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Recruitment Team Edit Form -->
|
|
<template id="team_edit_form">
|
|
<div class="form-grid">
|
|
<div class="form-group">
|
|
<label for="edit-primary-recruiter">Primary Recruiter:</label>
|
|
<select id="edit-primary-recruiter" class="form-select">
|
|
<t t-set="user_ids" t-value="request.env['res.users'].sudo().search([])"/>
|
|
<t t-foreach="user_ids" t-as="user">
|
|
<option t-att-value="user.id">
|
|
<t t-esc="user.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="edit-secondary-recruiters">Secondary Recruiters:</label>
|
|
<select id="edit-secondary-recruiters" class="form-select" multiple="multiple">
|
|
<t t-set="user_ids" t-value="request.env['res.users'].sudo().search([])"/>
|
|
<t t-foreach="user_ids" t-as="user">
|
|
<option t-att-value="user.id">
|
|
<t t-esc="user.display_name"/>
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Job Description Edit Form -->
|
|
<template id="description_edit_form">
|
|
<div class="form-group full-width">
|
|
<div id="edit-description-editor" class="oe_editor" style="min-height: 300px; border: 1px solid #ddd; padding: 8px;"></div>
|
|
<textarea id="edit-description" name="description" style="display:none;"></textarea>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
|
|
<template id="matching_candidates_popup">
|
|
<div class="popup-container bottom-right" id="matchingCandidatesPopup">
|
|
<div class="popup-header">
|
|
<h5>Matching Candidates</h5>
|
|
<button type="button" class="popup-close">&times;</button>
|
|
</div>
|
|
<div class="popup-body">
|
|
<div class="loading-spinner">
|
|
<div class="spinner"></div>
|
|
</div>
|
|
<div class="popup-content" style="display: none;"></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="applicants_popup">
|
|
<div class="popup-container bottom-right" id="applicantsPopup">
|
|
<div class="popup-header">
|
|
<h5>Applicants</h5>
|
|
<button type="button" class="popup-close">&times;</button>
|
|
</div>
|
|
<div class="popup-body">
|
|
<div class="loading-spinner">
|
|
<div class="spinner"></div>
|
|
</div>
|
|
<div class="popup-content" style="display: none;"></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
|
|
|
|
<!-- Matching Candidates Content Template -->
|
|
<template id="matching_candidates_content">
|
|
<div class="matching-candidates-container">
|
|
<!-- Search Box Only -->
|
|
<div class="mc-search-box">
|
|
<input type="text" id="mc-search" placeholder="Search candidates..." class="mc-search-input"/>
|
|
<span class="mc-search-icon">🔍</span>
|
|
<span class="mc-match-threshold">
|
|
Showing matches ≥ <t t-esc="min_match"/>%
|
|
</span>
|
|
</div>
|
|
<div class="mc-match-threshold" style="padding: 10px; text-align: center;">
|
|
Active Records (<t t-esc="len(candidates)"/>)
|
|
</div>
|
|
|
|
<!-- Candidates Grid -->
|
|
<div class="mc-cards-container">
|
|
<t t-foreach="candidates" t-as="candidate">
|
|
<div class="mc-card"
|
|
t-att-data-id="candidate.id"
|
|
t-att-data-candidate = "candidate.display_name"
|
|
t-att-data-image = "candidate.candidate_image"
|
|
t-att-data-email = "match_data[candidate.id]['candidate_data']['email']"
|
|
t-att-data-phone = "match_data[candidate.id]['candidate_data']['phone']"
|
|
t-att-data-manager = "match_data[candidate.id]['candidate_data']['manager']"
|
|
t-att-data-applications = "match_data[candidate.id]['candidate_data']['applications']"
|
|
t-att-data-primary-percent = "match_data[candidate.id]['primary_pct']"
|
|
t-att-data-secondary-percent = "match_data[candidate.id]['secondary_pct']"
|
|
t-att-data-match-primary-skills = "match_data[candidate.id]['matched_primary_skills']"
|
|
t-att-data-match-secondary-skills = "match_data[candidate.id]['matched_secondary_skills']"
|
|
t-on-click="onCandidateClick">
|
|
<!-- Candidate Avatar with Percentage Circles -->
|
|
<div class="mc-avatar-wrapper">
|
|
<div class="mc-avatar">
|
|
<t t-if="candidate.candidate_image">
|
|
<img t-att-src="image_data_uri(candidate.candidate_image)"
|
|
class="mc-avatar-img" alt="Candidate"/>
|
|
</t>
|
|
<t t-else="">
|
|
<div class="mc-avatar-initials">
|
|
<t t-esc="candidate.display_name[0].upper() if candidate.display_name else '?'"/>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
<h3 class="mc-detail-name">
|
|
<t t-esc="candidate.display_name or 'Unnamed Candidate'"/>
|
|
</h3>
|
|
|
|
|
|
<!-- Percentage Circles -->
|
|
<div class="mc-percentage-circles">
|
|
<div class="mc-percentage-circle mc-primary-circle"
|
|
t-att-data-percent="match_data[candidate.id]['primary_pct']">
|
|
<span class="mc-percentage-value">
|
|
<t t-esc="int(match_data[candidate.id]['primary_pct'])"/>%
|
|
</span>
|
|
<span class="mc-percentage-label">Primary</span>
|
|
</div>
|
|
<div class="mc-percentage-circle mc-secondary-circle"
|
|
t-att-data-percent="match_data[candidate.id]['secondary_pct']">
|
|
<span class="mc-percentage-value">
|
|
<t t-esc="int(match_data[candidate.id]['secondary_pct'])"/>%
|
|
</span>
|
|
<span class="mc-percentage-label">Secondary</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
|
|
<!-- Empty State -->
|
|
<t t-if="not candidates">
|
|
<div class="mc-empty-state">
|
|
<div class="mc-empty-icon">😕</div>
|
|
<h4>No matching candidates found</h4>
|
|
<p>Try lowering the match threshold or expanding your search criteria</p>
|
|
</div>
|
|
</t>
|
|
|
|
<!-- Candidate Detail Modal (Hidden by default) -->
|
|
<div id="candidateDetailModal" class="mc-modal">
|
|
<div class="mc-modal-content">
|
|
<span class="mc-close-modal">&times;</span>
|
|
<div id="candidateDetailContent"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
|
|
<!-- Applicants Content Template -->
|
|
<template id="applicants_content">
|
|
<div class="applicants-container">
|
|
<h6>Applicants for <t t-esc="job_title"/> (<t t-esc="total_applicants"/>)</h6>
|
|
|
|
<div class="stages-container">
|
|
<t t-foreach="stages" t-as="stage">
|
|
<div class="stage-card mb-3">
|
|
<h6><t t-esc="stage['name']"/> (<t t-esc="len(stage['applicants'])"/>)</h6>
|
|
|
|
<div class="applicant-list">
|
|
<t t-foreach="stage['applicants']" t-as="applicant">
|
|
<div class="applicant-card">
|
|
<div class="applicant-info">
|
|
<strong><t t-esc="applicant['name']"/></strong>
|
|
<div class="text-muted small">
|
|
Applied: <t t-esc="applicant['application_date']"/>
|
|
</div>
|
|
</div>
|
|
<div class="applicant-actions">
|
|
<a t-att-href="applicant['resume_url']"
|
|
target="_blank"
|
|
class="btn btn-sm btn-outline-primary"
|
|
t-if="applicant['resume_url'] != '#'">
|
|
View Resume
|
|
</a>
|
|
<span class="text-muted" t-if="applicant['resume_url'] == '#'">
|
|
No Resume
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Error Template -->
|
|
<template id="error_template">
|
|
<div class="alert alert-danger">
|
|
<t t-esc="message"/>
|
|
</div>
|
|
</template>
|
|
</odoo>
|