Compare commits
No commits in common. "13687718967e391e76c648bfb88b0f37c3680145" and "a181e845698769d2dc6980ec56e9aa7fd0df8502" have entirely different histories.
1368771896
...
a181e84569
Binary file not shown.
@ -155,27 +155,19 @@ DATABASES = {
|
||||
|
||||
|
||||
|
||||
# AUTH_PASSWORD_VALIDATORS = [
|
||||
# {
|
||||
# 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
# },
|
||||
# {
|
||||
# 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
# },
|
||||
# {
|
||||
# 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
# },
|
||||
# {
|
||||
# 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
# },
|
||||
# ]
|
||||
|
||||
# settings.py
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,7 +1,3 @@
|
||||
from pathlib import Path
|
||||
from rich import print
|
||||
from django.conf import settings
|
||||
import os
|
||||
import uuid
|
||||
import random
|
||||
from datetime import date, timedelta
|
||||
@ -43,9 +39,9 @@ class Command(BaseCommand):
|
||||
self.stdout.write(f"Preparing to create {jobs_count} jobs and {candidates_count} candidates.")
|
||||
|
||||
# 1. Clear existing data (Optional, but useful for clean seeding)
|
||||
JobPosting.objects.all().delete()
|
||||
FormTemplate.objects.all().delete()
|
||||
Candidate.objects.all().delete()
|
||||
JobPosting.objects.all().delete()
|
||||
Source.objects.all().delete()
|
||||
self.stdout.write(self.style.WARNING("Existing JobPostings and Candidates cleared."))
|
||||
|
||||
# 2. Create Foreign Key dependency: Source
|
||||
@ -78,7 +74,8 @@ class Command(BaseCommand):
|
||||
|
||||
# Random dates
|
||||
start_date = fake.date_object()
|
||||
deadline_date = start_date + timedelta(days=random.randint(14, 60))
|
||||
deadline_date = start_date + timedelta(days=random.randint(14, 60))
|
||||
joining_date = deadline_date + timedelta(days=random.randint(30, 90))
|
||||
|
||||
# Use Faker's HTML generation for CKEditor5 fields
|
||||
description_html = f"<h1>{title} Role</h1>" + "".join(f"<p>{fake.paragraph(nb_sentences=3, variable_nb_sentences=True)}</p>" for _ in range(3))
|
||||
@ -91,11 +88,26 @@ class Command(BaseCommand):
|
||||
"department": department,
|
||||
"job_type": job_type,
|
||||
"workplace_type": random.choice(WORKPLACE_TYPES),
|
||||
"location_city": fake.city(),
|
||||
"location_state": fake.state_abbr(),
|
||||
"location_country": "Saudia Arabia",
|
||||
"description": description_html,
|
||||
"qualifications": qualifications_html,
|
||||
"salary_range": salary_range,
|
||||
"benefits": benefits_html,
|
||||
"application_url": fake.url(),
|
||||
"application_start_date": start_date,
|
||||
"application_deadline": deadline_date,
|
||||
"application_instructions": instructions_html,
|
||||
"created_by": "Faker Script",
|
||||
"status": random.choice(STATUS_CHOICES),
|
||||
"hash_tags": f"#{department.lower().replace(' ', '')},#jobopening,#{fake.word()}",
|
||||
"position_number": f"{department[:3].upper()}{random.randint(100, 999)}",
|
||||
"reporting_to": random.choice(REPORTING_TO),
|
||||
"joining_date": joining_date,
|
||||
"open_positions": random.randint(1, 5),
|
||||
"source": default_source,
|
||||
"published_at": timezone.now() if random.random() < 0.7 else None,
|
||||
}
|
||||
|
||||
job = JobPosting.objects.create(
|
||||
@ -111,33 +123,28 @@ class Command(BaseCommand):
|
||||
for i in range(candidates_count):
|
||||
# Link candidate to a random job
|
||||
target_job = random.choice(created_jobs)
|
||||
print(target_job)
|
||||
|
||||
first_name = fake.first_name()
|
||||
last_name = fake.last_name()
|
||||
path = os.path.join(settings.BASE_DIR,'media/resumes/')
|
||||
|
||||
# path = Path('media/resumes/') # <-- CORRECT
|
||||
file = random.choice(os.listdir(path))
|
||||
print(file)
|
||||
# file = os.path.abspath(file)
|
||||
|
||||
candidate_data = {
|
||||
"first_name": first_name,
|
||||
"last_name": last_name,
|
||||
# Create a plausible email based on name
|
||||
"email": f"{first_name.lower()}.{last_name.lower()}@{fake.domain_name()}",
|
||||
"phone": "0566987458",
|
||||
"phone": fake.phone_number(),
|
||||
"address": fake.address(),
|
||||
# Placeholder resume path
|
||||
"resume": 'resumes/'+ file,
|
||||
# Placeholder resume path
|
||||
'match_score': random.randint(0, 100),
|
||||
"resume": f"resumes/{last_name.lower()}_{target_job.internal_job_id}_{fake.file_name(extension='pdf')}",
|
||||
"job": target_job,
|
||||
}
|
||||
print(candidate_data)
|
||||
|
||||
Candidate.objects.create(**candidate_data)
|
||||
self.stdout.write(self.style.NOTICE(
|
||||
f'Created Candidate {i+1}/{candidates_count}: {first_name} for {target_job.title[:30]}...'
|
||||
))
|
||||
print("done")
|
||||
|
||||
else:
|
||||
self.stdout.write(self.style.WARNING("No jobs created, skipping candidate generation."))
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.2.7 on 2025-10-23 14:08
|
||||
# Generated by Django 5.2.7 on 2025-10-22 16:33
|
||||
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
@ -221,7 +221,6 @@ class Migration(migrations.Migration):
|
||||
migrations.CreateModel(
|
||||
name='JobPosting',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated at')),
|
||||
('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')),
|
||||
@ -239,7 +238,7 @@ class Migration(migrations.Migration):
|
||||
('application_url', models.URLField(blank=True, help_text='URL where candidates apply', null=True, validators=[django.core.validators.URLValidator()])),
|
||||
('application_deadline', models.DateField(db_index=True)),
|
||||
('application_instructions', django_ckeditor_5.fields.CKEditor5Field(blank=True, null=True)),
|
||||
('internal_job_id', models.CharField(editable=False, max_length=50)),
|
||||
('internal_job_id', models.CharField(editable=False, max_length=50, primary_key=True, serialize=False)),
|
||||
('created_by', models.CharField(blank=True, help_text='Name of person who created this job', max_length=100)),
|
||||
('status', models.CharField(choices=[('DRAFT', 'Draft'), ('ACTIVE', 'Active'), ('CLOSED', 'Closed'), ('CANCELLED', 'Cancelled'), ('ARCHIVED', 'Archived')], db_index=True, default='DRAFT', max_length=20)),
|
||||
('hash_tags', models.CharField(blank=True, help_text='Comma-separated hashtags for linkedin post like #hiring,#jobopening', max_length=200, validators=[recruitment.validators.validate_hash_tags])),
|
||||
|
||||
@ -92,7 +92,7 @@ class JobPosting(Base):
|
||||
)
|
||||
|
||||
# Internal Tracking
|
||||
internal_job_id = models.CharField(max_length=50, editable=False)
|
||||
internal_job_id = models.CharField(max_length=50, primary_key=True, editable=False)
|
||||
created_by = models.CharField(
|
||||
max_length=100, blank=True, help_text="Name of person who created this job"
|
||||
)
|
||||
@ -193,29 +193,26 @@ class JobPosting(Base):
|
||||
return self.source.name if self.source else "System"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# from django.db import transaction
|
||||
|
||||
# Generate unique internal job ID if not exists
|
||||
# with transaction.atomic():
|
||||
# if not self.internal_job_id:
|
||||
# prefix = "KAAUH"
|
||||
# year = timezone.now().year
|
||||
# # Get next sequential number
|
||||
# last_job = (
|
||||
# JobPosting.objects.select_for_update().filter(
|
||||
# internal_job_id__startswith=f"{prefix}-{year}-"
|
||||
# )
|
||||
# .order_by("internal_job_id")
|
||||
# .last()
|
||||
# )
|
||||
if not self.internal_job_id:
|
||||
prefix = "KAAUH"
|
||||
year = timezone.now().year
|
||||
# Get next sequential number
|
||||
last_job = (
|
||||
JobPosting.objects.filter(
|
||||
internal_job_id__startswith=f"{prefix}-{year}-"
|
||||
)
|
||||
.order_by("internal_job_id")
|
||||
.last()
|
||||
)
|
||||
|
||||
# if last_job:
|
||||
# last_num = int(last_job.internal_job_id.split("-")[-1])
|
||||
# next_num = last_num + 1
|
||||
# else:
|
||||
# next_num = 1
|
||||
if last_job:
|
||||
last_num = int(last_job.internal_job_id.split("-")[-1])
|
||||
next_num = last_num + 1
|
||||
else:
|
||||
next_num = 1
|
||||
|
||||
# self.internal_job_id = f"{prefix}-{year}-{next_num:06d}"
|
||||
self.internal_job_id = f"{prefix}-{year}-{next_num:06d}"
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
|
||||
@receiver(post_save, sender=JobPosting)
|
||||
def format_job(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
# FormTemplate.objects.create(job=instance, is_active=False, name=instance.title)
|
||||
FormTemplate.objects.create(job=instance, is_active=True, name=instance.title)
|
||||
async_task(
|
||||
'recruitment.tasks.format_job_description',
|
||||
instance.pk,
|
||||
|
||||
@ -290,7 +290,7 @@ def handle_reume_parsing_and_scoring(pk):
|
||||
"strengths": "Brief summary of strengths",
|
||||
"weaknesses": "Brief summary of weaknesses",
|
||||
"years_of_experience": "Total years of experience (float, e.g., 6.5)",
|
||||
"criteria_checklist": List of job requirements if any {{ "Python": "Met", "AWS": "Not Met"}} only output the criteria_checklist in one of ('Met','Not Met') don't output any extra text,
|
||||
"criteria_checklist": {{ "Python": "Met", "AWS": "Not Mentioned"}},
|
||||
"category": "Most fitting professional field (e.g., Data Science), only output the category name and no other text example ('Software Development', 'correct') , ('Software Development and devops','wrong') ('Software Development / Backend Development','wrong')",
|
||||
"most_recent_job_title": "Candidate's most recent job title",
|
||||
"recommendation": "Detailed hiring recommendation narrative",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
resumes/cv.pdf
BIN
resumes/cv.pdf
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -111,31 +111,12 @@
|
||||
|
||||
{# ------------------- ADD EMAIL FORM ------------------- #}
|
||||
<h5 class="fw-bold mb-3">{% translate "Add Email Address" %}</h5>
|
||||
<form method="post" action="{% url 'account_email' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
{# 1. Explicitly render non-field errors first #}
|
||||
{% if form.non_field_errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{% for error in form.non_field_errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# 2. Render the fields using crispy #}
|
||||
{{ form|crispy }}
|
||||
|
||||
{# 3. If there are any global errors (not common here, but safe to include) #}
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-{{ message.tags }} mt-3">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{# Teal/Dark Green button consistent with "Save Changes" #}
|
||||
<button class="btn btn-success mt-3" type="submit" style="background-color: #008080; border-color: #008080;">{% translate "Add Email" %}</button>
|
||||
</form>
|
||||
<form method="post" action="{% url 'account_email' %}">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
{# Teal/Dark Green button consistent with "Save Changes" #}
|
||||
<button class="btn btn-success mt-3" type="submit" style="background-color: #008080; border-color: #008080;">{% translate "Add Email" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,8 +2,6 @@
|
||||
{% load static %}
|
||||
{% autoescape off %}
|
||||
|
||||
{# Use the built-in context variable 'password_reset_url' that allauth provides. #}
|
||||
|
||||
<div style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: auto; border: 1px solid #ddd; border-radius: 8px; overflow: hidden;">
|
||||
|
||||
<div style="background-color: #00636e; padding: 20px; color: white; text-align: center;">
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{% load static i18n %}
|
||||
{% load crispy_forms_tags %} <!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
@ -82,8 +82,52 @@
|
||||
<form method="post" action=".">
|
||||
{% csrf_token %}
|
||||
|
||||
{# RENDER THE FORM USING CRISPY FORMS #}
|
||||
{{ form|crispy }}
|
||||
{# Non-Field Errors (General errors like tokens or passwords not matching) #}
|
||||
{% if form.non_field_errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{% for error in form.non_field_errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# Password 1 Field #}
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.password.id_for_label }}" class="form-label fw-semibold">{% trans "New Password" %} *</label>
|
||||
|
||||
{# **CRITICAL FIX:** Iterate over the errors to display them correctly #}
|
||||
{% if form.password.errors %}
|
||||
<div class="alert alert-danger p-2 small">
|
||||
{% for error in form.password.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<input type="password" name="{{ form.password.name }}" id="{{ form.password.id_for_label }}"
|
||||
class="form-control" placeholder="{% trans 'Enter new password' %}" required autofocus>
|
||||
</div>
|
||||
|
||||
{# Password 2 Field #}
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.password2.id_for_label }}" class="form-label fw-semibold">{% trans "Confirm New Password" %} *</label>
|
||||
|
||||
{# **CRITICAL FIX:** Iterate over the errors to display them correctly #}
|
||||
{% if form.password2.errors %}
|
||||
<div class="alert alert-danger p-2 small">
|
||||
{% for error in form.password2.errors %}
|
||||
{{ error }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<input type="password" name="{{ form.password2.name }}" id="{{ form.password2.id_for_label }}"
|
||||
class="form-control" placeholder="{% trans 'Confirm new password' %}" required>
|
||||
</div>
|
||||
|
||||
{# Hidden fields MUST be present for the POST request to be valid #}
|
||||
{{ form.uid }}
|
||||
{{ form.token }}
|
||||
|
||||
{# Submit Button #}
|
||||
<button type="submit" class="btn btn-primary w-100 mt-4">
|
||||
|
||||
@ -146,12 +146,9 @@
|
||||
<div class="container-fluid py-4">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item text-decorat"><a href="{% url 'job_detail' submission.template.job.slug %}" class="text-secondary">Job Detail</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'form_builder' submission.template.pk%}" class="text-secondary">Form Template</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page" style="
|
||||
color: #F43B5E; /* Rosy Accent Color */
|
||||
font-weight: 600;
|
||||
">Submission Details</li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'job_detail' submission.template.job.slug %}">Job Detail</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'form_builder' submission.template.pk%}">Form Template</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Submission Details</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<div class="row">
|
||||
|
||||
@ -185,12 +185,9 @@
|
||||
<div class="container py-4">
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}" class="text-secondary">{% trans "Dashboard" %}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'form_templates_list' %}" class="text-secondary">{% trans "Form Templates" %}</a></li>
|
||||
<li class="breadcrumb-item active" style="
|
||||
color: #F43B5E; /* Rosy Accent Color */
|
||||
font-weight: 600;
|
||||
">{% trans "Submissions" %}</li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}">{% trans "Dashboard" %}</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'form_templates_list' %}">{% trans "Form Templates" %}</a></li>
|
||||
<li class="breadcrumb-item active">{% trans "Submissions" %}</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
|
||||
@ -364,7 +364,6 @@
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="resume-container">
|
||||
<div class="header">
|
||||
<div class="header-content">
|
||||
|
||||
@ -152,10 +152,7 @@
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}" class="text-secondary">Home</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'job_list' %}" class="text-secondary">Jobs</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page" style="
|
||||
color: #F43B5E; /* Rosy Accent Color */
|
||||
font-weight: 600;
|
||||
">Job Detail</li>
|
||||
<li class="breadcrumb-item active" aria-current="page">Job Detail</li>
|
||||
</ol>
|
||||
</nav>
|
||||
<div class="row g-4">
|
||||
|
||||
@ -186,10 +186,7 @@
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}" class="text-secondary">Home</a></li>
|
||||
<li class="breadcrumb-item"><a href="{% url 'job_detail' candidate.job.slug %}" class="text-secondary">Job:({{candidate.job.title}})</a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page" class="text-secondary" style="
|
||||
color: #F43B5E; /* Rosy Accent Color */
|
||||
font-weight: 600;
|
||||
">Applicant Detail</li>
|
||||
<li class="breadcrumb-item active" aria-current="page" class="text-secondary">Applicant Detail</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
{% load i18n %}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
@ -518,10 +517,7 @@
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-kaauh-light-bg font-sans">
|
||||
|
||||
<div class="container container-fluid flex-grow-1" style="max-width: 1600px; margin: 0 auto;">
|
||||
|
||||
{% include 'recruitment/partials/ai_overview_breadcromb.html' %}
|
||||
<!-- Header Section -->
|
||||
<header class="header-box">
|
||||
<div class="header-info">
|
||||
@ -719,10 +715,22 @@
|
||||
<span class="metric-value" style="color: var(--kaauh-teal);">{{ candidate.analysis_data.category }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<div class="analysis-metric" style="margin-bottom: 0.5rem; border-bottom: none;">
|
||||
<span class="metric-title">Match Score</span>
|
||||
<span class="metric-value">{{ candidate.analysis_data.match_score|default:0 }}/100</span>
|
||||
</div>
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar progress-bar-animated"
|
||||
style="width: {{ candidate.analysis_data.match_score|default:0 }}%; background-color:
|
||||
{% if candidate.analysis_data.match_score|default:0 < 50 %}var(--score-red){% elif candidate.analysis_data.match_score|default:0 < 75 %}var(--score-yellow){% else %}var(--score-green){% endif %}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if candidate.analysis_data.red_flags %}
|
||||
<div class="narrative-box">
|
||||
<div class="narrative-box red-flag-box">
|
||||
<h3 class="flag-title red"><i class="fas fa-flag"></i>Red Flags</h3>
|
||||
<!-- scoring_data.red_flags -->
|
||||
<p class="narrative-text">{{ candidate.analysis_data.red_flags|join:". "|default:"None." }}</p>
|
||||
|
||||
@ -1,118 +0,0 @@
|
||||
{% load i18n %}
|
||||
{% with request.resolver_match as resolved %}
|
||||
{% if 'resume-template' in resolved.route and resolved.kwargs.slug == candidate.slug %}
|
||||
<nav aria-label="breadcrumb" style="font-family: 'Inter', sans-serif; margin-bottom:1.5rem;">
|
||||
<ol style="
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
font-size: 0.875rem; /* text-sm */
|
||||
">
|
||||
|
||||
<li style="display: flex; align-items: center;">
|
||||
<a
|
||||
href="{% url 'dashboard' %}"
|
||||
style="
|
||||
color: #6c757d; /* text-secondary/gray */
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease-in-out;
|
||||
"
|
||||
onmouseover="this.style.color='#000000';"
|
||||
onmouseout="this.style.color='#6c757d';"
|
||||
>
|
||||
{% translate 'Home' %}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
">
|
||||
<span style="color: #6c757d; opacity: 0.75;" aria-hidden="true">/</span>
|
||||
</li>
|
||||
|
||||
<li style="display: flex; align-items: center;">
|
||||
<a
|
||||
href="{% url 'job_list' %}"
|
||||
style="
|
||||
color: #6c757d; /* text-secondary/gray */
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease-in-out;
|
||||
"
|
||||
onmouseover="this.style.color='#000000';"
|
||||
onmouseout="this.style.color='#6c757d';"
|
||||
>
|
||||
{% trans 'Jobs' %}
|
||||
</a>
|
||||
</li>
|
||||
<li style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
">
|
||||
<span style="color: #6c757d; opacity: 0.75;" aria-hidden="true">/</span>
|
||||
</li>
|
||||
<li style="display: flex; align-items: center;">
|
||||
<a
|
||||
href="{% url 'candidate_list' %}"
|
||||
style="
|
||||
color: #6c757d; /* text-secondary/gray */
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease-in-out;
|
||||
"
|
||||
onmouseover="this.style.color='#000000';"
|
||||
onmouseout="this.style.color='#6c757d';"
|
||||
>
|
||||
{% trans 'Applicants' %}
|
||||
</a>
|
||||
</li>
|
||||
<li style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
">
|
||||
<span style="color: #6c757d; opacity: 0.75;" aria-hidden="true">/</span>
|
||||
</li>
|
||||
<li style="display: flex; align-items: center;">
|
||||
<a
|
||||
href="{% url 'candidate_detail' candidate.slug %}"
|
||||
style="
|
||||
color: #6c757d; /* text-secondary/gray */
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease-in-out;
|
||||
"
|
||||
onmouseover="this.style.color='#000000';"
|
||||
onmouseout="this.style.color='#6c757d';"
|
||||
>
|
||||
{{candidate.resume_data.full_name}}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: 0.5rem;
|
||||
padding-right: 0.5rem;
|
||||
">
|
||||
<span style="color: #6c757d; opacity: 0.75;" aria-hidden="true">/</span>
|
||||
</li>
|
||||
|
||||
<li
|
||||
aria-current="page"
|
||||
style="
|
||||
color: #F43B5E; /* Rosy Accent Color */
|
||||
font-weight: 600;
|
||||
"
|
||||
>
|
||||
{% trans 'AI Overview' %}
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
Loading…
x
Reference in New Issue
Block a user