odoo18/addons_extensions/hr_recruitment_web_app/views/candidate.xml

606 lines
34 KiB
XML

<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<template id="candidates_list_partial_page">
<div class="ats-list-container">
<!-- Job List and Details -->
<div class="ats-list-body">
<!-- Job List Panel -->
<div class="ats-list-left expanded" id="candidates-list-panel">
<t t-call="hr_recruitment_web_app.candidates_list"/>
<!-- Toggle Button -->
<button id="candidates-list-sidebar-toggle-btn" class="ats-list-toggle-btn"></button>
</div>
<!-- Job Detail Panel -->
<div id="candidates-detail" class="ats-detail">
<em>Select a candidate to view details.</em>
</div>
</div>
</div>
<t t-call="hr_recruitment_web_app.candidate_form_template"/>
</template>
<template id="candidates_list">
<div class="ats-list-container">
<!-- Search -->
<div class="ats-list-search">
<input type="text" id="candidates-search" placeholder="🔍 Search candidates..."/>
</div>
<!-- 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(candidates)"/>
</span>
)
</button>
<button class="btn add-create-btn" type="button" id="add-candidate-create-btn" style="right:0;">
<span class="plus-icon">+</span>
Add New candidate
</button>
</div>
<ul class="ats-list">
<t t-foreach="candidates" t-as="candidate">
<li class="ats-item candidates-item" t-att-data-id="candidate.id">
<div class="ats-item-content">
<div class="ats-info">
<div id="candidate_name" class="ats-title" style="padding-bottom:10px;">
<strong>
<t t-if="candidate.display_name">
<t t-esc="candidate.display_name"/>
</t>
</strong>
</div>
<div style="display:none;">
<span id="create_date" t-esc="candidate.create_date"/>
<span id="candidate_email" t-esc="candidate.email_from"/>
<span id="candidate_phone" t-esc="candidate.partner_phone"/>
<span id="alternate_phone" t-esc="candidate.alternate_phone"/>
<span id="recruiter_id" t-esc="candidate.user_id.id"/>
<span id="recruiter_name" t-esc="candidate.user_id.display_name"/>
<span id="recruiter_image" t-esc="candidate.user_id.image_128"/>
</div>
</div>
<div class="ats-avatar">
<t t-if="candidate.candidate_image">
<img t-att-src="image_data_uri(candidate.candidate_image)"
class="ats-item-image"
alt="candidate photo"/>
</t>
<t t-else="">
<div class="ats-item-initials">
<t t-if="candidate.display_name">
<t t-esc="candidate.display_name[0].upper()"/>
</t>
</div>
</t>
</div>
</div>
</li>
</t>
</ul>
</div>
</template>
<template id="candidates_detail_partial">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"/>
<div class="ats-grid" id="ats-details-container">
<button type="button" class="close-detail" aria-label="Close">
<span aria-hidden="true">&amp;times;</span>
</button>
<div class="ats-card span-3" style="grid-row: span 2;" 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="#candidate-basic-info" class="nav-link smooth-scroll"><i
class="fa fa-info-circle me-2"></i>Basic Info
</a>
</li>
<li>
<a href="#candidate-contact" class="nav-link smooth-scroll"><i
class="fa fa-address-book me-2"></i>Contact
</a>
</li>
<li>
<a href="#candidate-resume" class="nav-link smooth-scroll"><i
class="fa fa-file-pdf me-2"></i>Resume
</a>
</li>
<li>
<a href="#candidate-availability" class="nav-link smooth-scroll"><i
class="fa fa-calendar me-2"></i>Availability
</a>
</li>
<li>
<a href="#candidate-skills" class="nav-link smooth-scroll">
<i class="fa fa-star me-2"></i>
Skills
</a>
</li>
<li>
<a href="#candidate-employment" class="nav-link smooth-scroll"><i
class="fa fa-briefcase me-2"></i>Employment
</a>
</li>
<li>
<a href="#candidate-education" class="nav-link smooth-scroll"><i
class="fa fa-graduation-cap me-2"></i>Education
</a>
</li>
<li>
<a href="#candidate-family" class="nav-link smooth-scroll"><i
class="fa fa-users me-2"></i>Family
</a>
</li>
</ul>
</div>
</div>
<div class="ats-card span-9" style="grid-row: span 1;" id="candidate-profile">
<div class="ats-info">
<h1 class="ats-title">
<span t-esc="candidate.partner_name or 'Unnamed Candidate'"/>
</h1>
<div class="ats-meta">
<span class="me-3 meta-item">
<i class="fa fa-envelope me-1"></i>
<span t-esc="candidate.email_from or 'No email'"/>
</span>
<span class="me-3 meta-item">
<i class="fa fa-phone me-1"></i>
<span t-esc="candidate.partner_phone or 'No phone'"/>
</span>
<span class="me-3 meta-item">
<i class="fa fa-user-tie me-1"></i>
<span t-esc="candidate.type_id.display_name or 'No type specified'"/>
</span>
<span class="me-3 meta-item" t-if="candidate.employee_id">
<i class="fa fa-id-card me-1"></i>
<t t-if="candidate.employee_id.employee_id">
<span t-esc="candidate.employee_id.employee_id or 'No employee record'"/>
</t>
<t t-else="">
<span t-esc="'Not Specified'"/>
</t>
</span>
</div>
</div>
<div class="ats-avatar">
<div class="avatar-wrapper">
<img t-if="candidate.candidate_image"
t-att-src="image_data_uri(candidate.candidate_image)"
class="rounded-circle avatar-img animate__animated animate__fadeIn"
width="120" height="120" alt="Candidate Photo"/>
<div t-else=""
class="avatar-placeholder rounded-circle animate__animated animate__fadeIn">
<div class="applicant-img-placeholder">
<t t-if="candidate.display_name">
<t t-esc="candidate.display_name[0].upper()"/>
</t>
<t t-else="">
C
</t>
</div>
</div>
<div class="avatar-overlay">
<i class="fa fa-search-plus"></i>
</div>
</div>
</div>
</div>
<!-- <div class="ats-card span-3" style="grid-row: span 1;">-->
<!-- </div>-->
<div class="ats-card span-6" style="grid-row: span 1;" id="candidate-basic-info">
<h4 class="section-title">
<i class="fa fa-info-circle me-2"></i>Basic Information
</h4>
<div class="detail-grid">
<div class="detail-row">
<span class="detail-label">Type:</span>
<span class="detail-value" t-esc="candidate.type_id.display_name or 'N/A'"/>
</div>
<div class="detail-row">
<span class="detail-label">Evaluation:</span>
<span class="priority-stars">
<t t-set="priority_value" t-value="int(candidate.priority) if candidate.priority else 0"/>
<t t-foreach="range(5)" t-as="i">
<i t-att-class="'fa ' + ('fa-star' if priority_value > i else 'fa-star-o')"
t-att-style="'color: ' + ('var(--secondary-yellow)' if priority_value > i else 'var(--gray-300)') + '; ' +
('text-shadow: 0 0 8px var(--secondary-yellow); animation: pulse 1.5s infinite alternate;' if priority_value > i else '')"/>
</t>
</span>
</div>
</div>
</div>
<div class="ats-card span-6" style="grid-row: span 2;" id="candidate-contact">
<h4 class="section-title">
<i class="fa fa-address-book me-2"></i>Contact Information
</h4>
<div class="detail-grid">
<div class="detail-row">
<span class="detail-label">Email:</span>
<span class="detail-value" t-esc="candidate.email_from or 'N/A'"/>
</div>
<div class="detail-row">
<span class="detail-label">Phone:</span>
<span class="detail-value" t-esc="candidate.partner_phone or 'N/A'"/>
</div>
<div class="detail-row">
<span class="detail-label">Alternate Phone:</span>
<span class="detail-value" t-esc="candidate.alternate_phone or 'N/A'"/>
</div>
<div class="detail-row">
<span class="detail-label">LinkedIn:</span>
<t t-if="candidate.linkedin_profile">
<a t-att-href="candidate.linkedin_profile" target="_blank"
class="detail-value social-link">
<i class="fa fa-linkedin-square me-1"></i>
<span>View Profile</span>
</a>
</t>
<t t-else="">
<span class="detail-value">N/A</span>
</t>
</div>
<div class="detail-row">
<span class="detail-label">Manager:</span>
<span class="detail-value" t-esc="candidate.user_id.display_name or 'Not assigned'"/>
</div>
</div>
</div>
<div class="ats-card span-3" id="candidate-resume">
<h4 class="section-title">
<i class="fa fa-file-pdf me-2"></i>Resume
</h4>
<div class="attachment-item">
<div class="attachment-info">
<div class="attachment-name attachment-header"
t-esc="candidate.resume_name or 'Resume'"/>
<div class="attachment-actions">
<!-- Preview Button -->
<a t-if="candidate.resume"
t-att-href="'/web/content/?model=hr.candidate&amp;id=' + str(candidate.id) + '&amp;field=resume&amp;download=false'"
class="btn btn-sm btn-outline-primary me-2 document-action-btn attachment-btn preview-btn"
target="_blank">
<i class="fa fa-eye me-1"></i>Preview
</a>
<!-- Download Button -->
<a t-if="candidate.resume"
t-att-href="'/web/content/?model=hr.candidate&amp;id=' + str(candidate.id) + '&amp;field=resume&amp;download=true'"
class="btn btn-sm btn-outline-success document-action-btn attachment-btn download-btn">
<i class="fa fa-download me-1"></i>Download
</a>
</div>
</div>
</div>
</div>
<div class="ats-card span-3" id="candidate-availability">
<h4 class="section-title">
<i class="fa fa-calendar me-2"></i>Availability
</h4>
<div class="detail-grid">
<div class="detail-row">
<span class="detail-label">Status:</span>
<span class="detail-value" t-esc="candidate.availability or 'Not specified'"/>
</div>
<div class="detail-row">
<span class="detail-label">Categories:</span>
<div class="skills-list">
<t t-foreach="candidate.categ_ids" t-as="category">
<span class="category-badge animate__animated animate__fadeInUp"
t-att-data-delay="category_index * 50"
t-esc="category.display_name"/>
</t>
<t t-if="not candidate.categ_ids">
<span class="text-muted">None specified</span>
</t>
</div>
</div>
</div>
</div>
<div class="ats-card span-12" id="candidate-skills">
<h4 class="section-title">
<i class="fa fa-star me-2"></i>Skills
<small class="text-muted float-end">
<span t-esc="len(candidate.candidate_skill_ids) or '0'"/>
skills recorded
</small>
</h4>
<div t-if="candidate.candidate_skill_ids and len(candidate.candidate_skill_ids) > 0"
class="skills-container">
<t t-foreach="candidate.candidate_skill_ids" t-as="skill">
<div class="skill-item animate__animated animate__fadeInUp"
t-att-data-delay="skill_index * 50">
<div class="skill-info">
<span class="skill-name" t-esc="skill.skill_id.display_name"/>
<span class="skill-type" t-esc="skill.skill_type_id.display_name"/>
</div>
<div class="skill-level">
<span class="skill-level-name"
t-esc="skill.skill_level_id.display_name"/>
<div class="progress">
<div class="progress-bar progress-bar-animated" role="progressbar"
t-att-style="'width: ' + str(skill.level_progress) + '%;'"
t-att-aria-valuenow="skill.level_progress"
aria-valuemin="0" aria-valuemax="100">
</div>
</div>
</div>
</div>
</t>
</div>
<t t-else="">
<div class="alert alert-info animate__animated animate__fadeIn">No skills recorded
</div>
</t>
</div>
<div class="ats-card span-12" id="candidate-employment">
<h4 class="section-title">
<i class="fa fa-briefcase me-2"></i>Employment History
</h4>
<div t-if="candidate.employer_history and len(candidate.employer_history) > 0"
class="experience-list">
<t t-foreach="candidate.employer_history" t-as="exp">
<div class="experience-item animate__animated animate__fadeInLeft"
t-att-data-delay="exp_index * 100">
<div class="exp-header">
<h5 t-esc="exp.designation or 'No designation'"/>
<span class="exp-company" t-esc="exp.company_name or 'No company'"/>
</div>
<div class="exp-duration">
<i class="fa fa-calendar me-1"></i>
<span t-esc="exp.date_of_joining or 'Start date not specified'"/>
<span>to</span>
<span t-esc="exp.last_working_day or 'Present'"/>
</div>
<div class="exp-ctc" t-if="exp.ctc">
<i class="fa fa-money-bill-wave me-1"></i>
<span t-esc="exp.ctc"/>
</div>
</div>
</t>
</div>
<t t-else="">
<div class="alert alert-info animate__animated animate__fadeIn">No employment history recorded
</div>
</t>
</div>
<div class="ats-card span-12" id="candidate-education">
<h4 class="section-title">
<i class="fa fa-graduation-cap me-2"></i>Education History
</h4>
<div t-if="candidate.education_history and len(candidate.education_history) > 0"
class="education-list">
<t t-foreach="candidate.education_history" t-as="edu">
<div class="education-item animate__animated animate__fadeInRight"
t-att-data-delay="edu_index * 100">
<div class="edu-header">
<h5 t-esc="edu.name or 'No degree'"/>
<span class="edu-type" t-esc="edu.education_type or 'No type'"/>
</div>
<div class="edu-university" t-esc="edu.university or 'No university'"/>
<div class="edu-duration">
<i class="fa fa-calendar me-1"></i>
<span t-esc="edu.start_year or 'Start year not specified'"/>
<span>to</span>
<span t-esc="edu.end_year or 'Present'"/>
</div>
<div class="edu-marks" t-if="edu.marks_or_grade">
<i class="fa fa-award me-1"></i>
<span t-esc="edu.marks_or_grade"/>
</div>
</div>
</t>
</div>
<t t-else="">
<div class="alert alert-info animate__animated animate__fadeIn">No education history recorded
</div>
</t>
</div>
<div class="ats-card span-12" id="candidate-family">
<h4 class="section-title">
<i class="fa fa-users me-2"></i>Family Details
</h4>
<div t-if="candidate.family_details and len(candidate.family_details) > 0"
class="family-list">
<t t-foreach="candidate.family_details" t-as="member">
<div class="family-item animate__animated animate__fadeInUp"
t-att-data-delay="member_index * 50">
<div class="family-header">
<h5 t-esc="member.name or 'No name'"/>
<span class="family-relation" t-esc="member.relation_type or 'No relation specified'"/>
</div>
<div class="family-details">
<div class="detail-row">
<span class="detail-label">Contact:</span>
<span class="detail-value" t-esc="member.contact_no or 'N/A'"/>
</div>
<div class="detail-row">
<span class="detail-label">Date of Birth:</span>
<span class="detail-value" t-esc="member.dob or 'N/A'"/>
</div>
<div class="detail-row">
<span class="detail-label">Location:</span>
<span class="detail-value" t-esc="member.location or 'N/A'"/>
</div>
</div>
</div>
</t>
</div>
<t t-else="">
<div class="alert alert-info animate__animated animate__fadeIn">No family details recorded
</div>
</t>
</div>
</div>
</template>
<template id="candidate_form_template">
<div id="candidate-form-modal" class="candidate-form-modal">
<div class="candidate-form-content">
<div class="candidate-form-header">
<div class="header-icon-container">
<i class="fas fa-user-tie header-icon"></i>
<h3>Candidate Profile</h3>
</div>
<span class="candidate-form-close">&amp;times;</span>
</div>
<div class="candidate-form-body">
<form id="candidate-form" class="candidate-form">
<!-- Header Section with Avatar and Basic Info -->
<div class="form-section header-section">
<div class="avatar-container">
<div class="candidate-avatar">
<img id="candidate-image" src="/web/static/src/img/placeholder.png"
alt="Candidate Avatar"/>
<div class="avatar-upload">
<i class="fas fa-camera"></i>
<input type="file" id="avatar-upload" accept="image/*"/>
</div>
</div>
<div class="basic-info">
<div class="form-group">
<label for="candidate-sequence">Candidate ID</label>
<input type="text" id="candidate-sequence" class="form-input" readonly="1"/>
</div>
<div class="form-group">
<label for="partner-name">Full Name*</label>
<input type="text" id="partner-name" class="form-input" required="1"
placeholder="Candidate's Name"/>
</div>
</div>
</div>
</div>
<!-- Contact Information -->
<div class="form-section contact-section">
<h4 class="section-title">
<i class="fas fa-address-book"></i>
Contact Information
</h4>
<div class="form-grid">
<div class="form-group">
<label for="email">Email*</label>
<input type="email" id="email" class="form-input" required="1"
placeholder="Email address"/>
</div>
<div class="form-group">
<label for="phone">Phone*</label>
<input type="tel" id="phone" class="form-input" required="1"
placeholder="Phone number"/>
</div>
<div class="form-group">
<label for="alternate-phone">Alternate Phone</label>
<input type="tel" id="alternate-phone" class="form-input"
placeholder="Alternate phone"/>
</div>
<div class="form-group">
<label for="linkedin">LinkedIn Profile</label>
<input type="url" id="linkedin" class="form-input" placeholder="LinkedIn URL"/>
</div>
<div class="form-group">
<t t-set="type_ids" t-value="request.env['hr.recruitment.degree'].search([])"/>
<label for="type">Degree</label>
<select id="type" class="form-select"
data-placeholder="Select Degree" draggable="true">
<option value="">Select type</option>
<t t-foreach="type_ids" t-as="type_id" t-key="type_id.id">
<option t-att-value="type_id.id">
<t t-esc="type_id.display_name"/>
</option>
</t>
</select>
</div>
<div class="form-group">
<t t-set="recruitment_users" t-value="request.env['res.users'].sudo().search([('groups_id', 'in', request.env.ref('hr_recruitment.group_hr_recruitment_manager').id)])"/>
<label for="manager">Manager</label>
<select id="manager" class="form-select select2"
data-placeholder="Select Manager">
<option value="">Select Manager</option>
<t t-foreach="recruitment_users" t-as="user_id" t-key="user_id.id">
<option t-att-value="user_id.id"
t-att-data-image="'/web/image/res.users/' + str(user_id.id) + '/image_128'">
<t t-esc="user_id.display_name"/>
</option>
</t>
</select>
</div>
<div class="form-group">
<label for="availability">Availability</label>
<input type="date" id="availability" class="form-input" placeholder="Availability"/>
</div>
</div>
</div>
<!-- Skills Section -->
<div class="form-section skills-container" id="skills-container">
<t t-set="skills" t-value="request.env['hr.skill'].search([])"/>
<label>Skills</label>
<select id="candidate-skills" class="form-select select2" multiple="multiple"
data-placeholder="Select 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>
<!-- Resume Section -->
<div class="form-section resume-section">
<h4 class="section-title">
<i class="fas fa-file-alt"></i>
Resume
</h4>
<div class="resume-upload-container">
<div class="resume-upload-area" id="resume-dropzone">
<i class="fas fa-cloud-upload-alt upload-icon"></i>
<h5>Upload Resume</h5>
<p>Drag &amp; drop your resume here or click to browse</p>
<input type="file" id="resume-upload"
accept=".pdf,.doc,.docx,.jpg,.png,.jpeg,.txt"/>
</div>
<div class="resume-preview" id="resume-preview">
<div class="resume-preview-placeholder">
<i class="fas fa-file-alt"></i>
<p>Resume preview will appear here</p>
</div>
<iframe id="resume-iframe" style="display: none;"></iframe>
<img id="resume-image" style="display: none; max-width: 100%; max-height: 400px;"/>
<div id="unsupported-format" style="display: none;">
<i class="fas fa-exclamation-triangle"></i>
<p>Preview not available for this file type</p>
<a id="download-resume" href="#" class="btn btn-primary mt-2">
<i class="fas fa-download"></i>
Download File
</a>
</div>
</div>
</div>
</div>
</form>
</div>
<div class="form-actions">
<button type="button" class="btn btn-candidate-primary" id="upload-applicant-resume">
<i class="fas fa-upload"></i>
Upload Resume
</button>
<div class="footer-right">
<button type="button" class="btn-cancel">Cancel</button>
<button type="button" class="btn-candidate-primary btn-submit" id="save-candidate">
<i class="fas fa-save"></i>
Save Candidate
</button>
</div>
</div>
</div>
</div>
</template>
</odoo>