safe filter
This commit is contained in:
parent
3b8ed4c93b
commit
53318a998c
Binary file not shown.
@ -120,6 +120,8 @@ DATABASES = {
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
||||
|
||||
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
|
||||
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -5,7 +5,7 @@ from django.utils import timezone
|
||||
from .models import (
|
||||
JobPosting, Candidate, TrainingMaterial, ZoomMeeting,
|
||||
FormTemplate, FormStage, FormField, FormSubmission, FieldResponse,
|
||||
SharedFormTemplate, Source, HiringAgency, IntegrationLog,InterviewSchedule
|
||||
SharedFormTemplate, Source, HiringAgency, IntegrationLog,InterviewSchedule,Profile
|
||||
)
|
||||
|
||||
class FormFieldInline(admin.TabularInline):
|
||||
@ -261,4 +261,5 @@ admin.site.register(FormStage)
|
||||
admin.site.register(FormField)
|
||||
admin.site.register(FieldResponse)
|
||||
admin.site.register(InterviewSchedule)
|
||||
admin.site.register(Profile)
|
||||
# admin.site.register(HiringAgency)
|
||||
|
||||
@ -259,8 +259,9 @@ class JobPostingForm(forms.ModelForm):
|
||||
'type': 'date'
|
||||
}),
|
||||
'application_instructions': SummernoteWidget(attrs={
|
||||
# Removed 'class' and 'rows'
|
||||
'placeholder': 'Special instructions for applicants (e.g., required documents, reference requirements, etc.)'
|
||||
|
||||
'placeholder': 'Special instructions for applicants (e.g., required documents, reference requirements, etc.)',
|
||||
|
||||
}),
|
||||
'open_positions': forms.NumberInput(attrs={
|
||||
'class': 'form-control',
|
||||
|
||||
24
recruitment/migrations/0027_profile.py
Normal file
24
recruitment/migrations/0027_profile.py
Normal file
@ -0,0 +1,24 @@
|
||||
# Generated by Django 5.2.7 on 2025-10-08 13:01
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('recruitment', '0026_interviewschedule_scheduledinterview'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Profile',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('profile_image', models.ImageField(blank=True, null=True, upload_to='profile_pic/')),
|
||||
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@ -9,6 +9,9 @@ from django.core.exceptions import ValidationError
|
||||
from django_countries.fields import CountryField
|
||||
from django.urls import reverse
|
||||
|
||||
class Profile(models.Model):
|
||||
profile_image=models.ImageField(null=True,blank=True,upload_to='profile_pic/')
|
||||
user=models.OneToOneField(User,on_delete=models.CASCADE,related_name='profile')
|
||||
class Base(models.Model):
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Created at'))
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name=_('Updated at'))
|
||||
|
||||
@ -83,8 +83,7 @@ class ZoomMeetingListView(ListView):
|
||||
if search_query:
|
||||
queryset = queryset.filter(
|
||||
Q(topic__icontains=search_query) |
|
||||
Q(meeting_id__icontains=search_query) |
|
||||
Q(host_email__icontains=search_query)
|
||||
Q(meeting_id__icontains=search_query)
|
||||
)
|
||||
|
||||
return queryset
|
||||
@ -143,27 +142,30 @@ def ZoomMeetingDeleteView(request, pk):
|
||||
|
||||
|
||||
#Job Posting
|
||||
def job_list(request):
|
||||
"""Display the list of job postings order by creation date descending"""
|
||||
jobs=JobPosting.objects.all().order_by('-created_at')
|
||||
# def job_list(request):
|
||||
# """Display the list of job postings order by creation date descending"""
|
||||
# jobs=JobPosting.objects.all().order_by('-created_at')
|
||||
|
||||
# Filter by status if provided
|
||||
status=request.GET.get('status')
|
||||
if status:
|
||||
jobs=jobs.filter(status=status)
|
||||
# # Filter by status if provided
|
||||
# print(f"the request is: {request} ")
|
||||
# status=request.GET.get('status')
|
||||
# print(f"DEBUG: Status filter received: {status}")
|
||||
# if status:
|
||||
# jobs=jobs.filter(status=status)
|
||||
|
||||
#pagination
|
||||
paginator=Paginator(jobs,10) # Show 10 jobs per page
|
||||
page_number=request.GET.get('page')
|
||||
page_obj=paginator.get_page(page_number)
|
||||
return render(request, 'jobs/job_list.html', {
|
||||
'page_obj': page_obj,
|
||||
'status_filter': status
|
||||
})
|
||||
# #pagination
|
||||
# paginator=Paginator(jobs,10) # Show 10 jobs per page
|
||||
# page_number=request.GET.get('page')
|
||||
# page_obj=paginator.get_page(page_number)
|
||||
# return render(request, 'jobs/job_list.html', {
|
||||
# 'page_obj': page_obj,
|
||||
# 'status_filter': status
|
||||
# })
|
||||
|
||||
|
||||
def create_job(request):
|
||||
"""Create a new job posting"""
|
||||
|
||||
if request.method=='POST':
|
||||
|
||||
form=JobPostingForm(request.POST,is_anonymous_user=not request.user.is_authenticated)
|
||||
@ -761,7 +763,8 @@ def delete_form_template(request, template_id):
|
||||
def form_wizard_view(request, template_id):
|
||||
"""Display the form as a step-by-step wizard"""
|
||||
template = get_object_or_404(FormTemplate, id=template_id, is_active=True)
|
||||
return render(request, 'forms/form_wizard.html', {'template_id': template_id})
|
||||
job_id=template.job.internal_job_id
|
||||
return render(request, 'forms/form_wizard.html', {'template_id': template_id,'job_id':job_id})
|
||||
@require_http_methods(["POST"])
|
||||
def submit_form(request, template_id):
|
||||
"""Handle form submission"""
|
||||
|
||||
@ -41,10 +41,15 @@ class JobListView(LoginRequiredMixin, ListView):
|
||||
# Filter for non-staff users
|
||||
if not self.request.user.is_staff:
|
||||
queryset = queryset.filter(status='Published')
|
||||
|
||||
status=self.request.GET.get('status')
|
||||
if status:
|
||||
queryset=queryset.filter(status=status)
|
||||
|
||||
return queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['search_query'] = self.request.GET.get('search', '')
|
||||
context['lang'] = get_language()
|
||||
@ -288,7 +293,7 @@ class TrainingListView(LoginRequiredMixin, ListView):
|
||||
template_name = 'recruitment/training_list.html'
|
||||
context_object_name = 'materials'
|
||||
paginate_by = 10
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = super().get_queryset()
|
||||
|
||||
@ -296,8 +301,7 @@ class TrainingListView(LoginRequiredMixin, ListView):
|
||||
search_query = self.request.GET.get('search', '')
|
||||
if search_query:
|
||||
queryset = queryset.filter(
|
||||
Q(title__icontains=search_query) |
|
||||
Q(description__icontains=search_query)
|
||||
Q(title__icontains=search_query)
|
||||
)
|
||||
|
||||
# Filter for non-staff users
|
||||
|
||||
@ -320,11 +320,13 @@
|
||||
<li class="nav-item me-2">
|
||||
<a class="nav-link {% if request.resolver_match.url_name == 'form_templates_list' %}active{% endif %}" href="{% url 'form_templates_list' %}">
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" stroke="currentColor" stroke-width="2"></path>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
|
||||
</svg>
|
||||
|
||||
{% trans "Form Templates" %}
|
||||
</span>
|
||||
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown ms-2">
|
||||
@ -389,21 +391,35 @@
|
||||
data-bs-auto-close="outside"
|
||||
data-bs-offset="0, 8"
|
||||
>
|
||||
<div class="profile-avatar" title="{% trans 'Your account' %}">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
{% if user.profile.profile_image %}
|
||||
<img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar"
|
||||
style="width: 36px; height: 36px; object-fit: cover; background-color: var(--kaauh-teal); display: inline-block; vertical-align: middle;"
|
||||
title="{% trans 'Your account' %}">
|
||||
{% else %}
|
||||
<div class="profile-avatar" title="{% trans 'Your account' %}">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% comment %} <span class="ms-2 d-none d-lg-inline fw-semibold">{{ user.username }}</span> {% endcomment %}
|
||||
</button>
|
||||
<ul
|
||||
class="dropdown-menu dropdown-menu-end py-0 shadow border-0 rounded-3"
|
||||
data-bs-popper="static"
|
||||
style="min-width: 240px;"
|
||||
>
|
||||
<li class="px-4 py-3 bg-dark-subtle">
|
||||
<li class="px-4 py-3 ">
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="me-3">
|
||||
<div class="profile-avatar" style="width: 44px; height: 44px; background-color: var(--kaauh-teal);">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
<div class="me-3 d-flex align-items-center justify-content-center" style="min-width: 48px;">
|
||||
{% if user.profile.profile_image %}
|
||||
<img src="{{ user.profile.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar shadow-sm border"
|
||||
style="width: 44px; height: 44px; object-fit: cover; background-color: var(--kaauh-teal); display: block;"
|
||||
title="{% trans 'Your account' %}">
|
||||
{% else %}
|
||||
<div class="profile-avatar shadow-sm border d-flex align-items-center justify-content-center"
|
||||
style="width: 44px; height: 44px; background-color: var(--kaauh-teal); font-size: 1.2rem;">
|
||||
{{ user.username|first|upper }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-semibold text-dark">{{ user.get_full_name|default:user.username }}</div>
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
{% load static i18n %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -685,8 +686,11 @@
|
||||
<!-- Sidebar with form elements -->
|
||||
<div class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<a class="" href="{% url 'form_templates_list' %}"></a>
|
||||
<h2><i class="fas fa-cube"></i> Form Elements</h2>
|
||||
<a class="" href="{% url 'form_templates_list' %}">
|
||||
<img src="{% static 'image/kaauh.jpeg' %}" style="height:100px; width:100px;">
|
||||
|
||||
</a>
|
||||
|
||||
</div>
|
||||
<div class="field-categories">
|
||||
<div class="field-category">
|
||||
|
||||
@ -476,11 +476,15 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="/profile/">{% translate "Profile" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="https://kaauh.edu.sa/career">{% translate "Careers" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<nav id="bottomNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: var(--kaauh-teal); z-index: 1030;">
|
||||
<span class="ms-2 text-white">JOB ID: {{job_id}}</span>
|
||||
</nav>
|
||||
|
||||
<div class="page-content-wrapper">
|
||||
|
||||
@ -161,7 +161,7 @@
|
||||
<div class="col-12">
|
||||
<div>
|
||||
<label for="{{ form.description.id_for_label }}" class="form-label">{% trans "Job Description" %} <span class="text-danger">*</span></label>
|
||||
{{ form.description }}
|
||||
{{ form.description}}
|
||||
{% if form.description.errors %}<div class="text-danger small mt-1">{{ form.description.errors }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
@ -169,7 +169,7 @@
|
||||
<div class="col-12">
|
||||
<div>
|
||||
<label for="{{ form.qualifications.id_for_label }}" class="form-label">{% trans "Qualifications and Requirements" %}</label>
|
||||
{{ form.qualifications }}
|
||||
{{ form.qualifications}}
|
||||
{% if form.qualifications.errors %}<div class="text-danger small mt-1">{{ form.qualifications.errors }}</div>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -339,19 +339,19 @@
|
||||
{% if job.description %}
|
||||
<div class="mb-4">
|
||||
<h5>{% trans "Job Description" %}</h5>
|
||||
<div class="text-secondary">{{ job.description|linebreaks }}</div>
|
||||
<div class="text-secondary">{{ job.description|safe }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if job.qualifications %}
|
||||
<div class="mb-4">
|
||||
<h5>{% trans "Required Qualifications" %}</h5>
|
||||
<div class="text-secondary">{{ job.qualifications|linebreaks }}</div>
|
||||
<div class="text-secondary">{{ job.qualifications|safe }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if job.benefits %}
|
||||
<div class="mb-4">
|
||||
<h5>{% trans "Benefits" %}</h5>
|
||||
<div class="text-secondary">{{ job.benefits|linebreaks }}</div>
|
||||
<div class="text-secondary">{{ job.benefits|safe}}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -361,7 +361,7 @@
|
||||
<div class="tab-pane fade" id="instructions" role="tabpanel" aria-labelledby="instructions-tab">
|
||||
<div class="mb-4">
|
||||
<h5>{% trans "Application Instructions" %}</h5>
|
||||
<div class="text-secondary">{{ job.application_instructions|linebreaks }}</div>
|
||||
<div class="text-secondary">{{ job.application_instructions|safe }}</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -526,7 +526,7 @@
|
||||
{% trans "Manage the custom application forms associated with this job posting." %}
|
||||
</p>
|
||||
|
||||
<a href="" class="btn btn-main-action">
|
||||
<a href="{% url 'create_form_template' %}" class="btn btn-main-action">
|
||||
<i class="fas fa-plus-circle me-2"></i> {% trans "Create New Form" %}
|
||||
</a>
|
||||
|
||||
@ -534,9 +534,9 @@
|
||||
<i class="fas fa-list-alt me-1"></i> {% trans "View All Existing Forms" %}
|
||||
</a>
|
||||
|
||||
<a href="{% url 'candidate_create_for_job' job.slug %}" class="btn btn-success me-2">
|
||||
<a href="{% url 'candidate_create_for_job' job.slug %}" class="btn btn-main-action">
|
||||
<i class="fas fa-user-plus"></i> {% trans "Create Candidate" %}
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -151,6 +151,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="/profile/">{% translate "Profile" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link text-secondary" href="https://kaauh.edu.sa/career">{% translate "Careers" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -233,10 +236,10 @@
|
||||
{% comment %} <div class="col"> <i class="fas fa-user-tie text-muted me-2"></i> <strong>Created By:</strong> {{ job.created_by|default:"N/A" }} </div> {% endcomment %}
|
||||
</div>
|
||||
|
||||
{% if job.description %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-info-circle me-2"></i>Job Description</h5><div class="text-secondary">{{ job.description|linebreaks }}</div></div>{% endif %}
|
||||
{% if job.qualifications %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-graduation-cap me-2"></i>Qualifications</h5><div class="text-secondary">{{ job.qualifications|linebreaks }}</div></div>{% endif %}
|
||||
{% if job.benefits %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-hand-holding-usd me-2"></i>Benefits</h5><div class="text-secondary">{{ job.benefits|linebreaks }}</div></div>{% endif %}
|
||||
{% if job.application_instructions %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-file-alt me-2"></i>Application Instructions</h5><div class="text-secondary">{{ job.application_instructions|linebreaks }}</div></div>{% endif %}
|
||||
{% if job.description %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-info-circle me-2"></i>Job Description</h5><div class="text-secondary">{{ job.description|safe }}</div></div>{% endif %}
|
||||
{% if job.qualifications %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-graduation-cap me-2"></i>Qualifications</h5><div class="text-secondary">{{ job.qualifications|safe }}</div></div>{% endif %}
|
||||
{% if job.benefits %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-hand-holding-usd me-2"></i>Benefits</h5><div class="text-secondary">{{ job.benefits|safe }}</div></div>{% endif %}
|
||||
{% if job.application_instructions %}<hr class="my-4"><div class="mb-4"><h5 class="fw-bold" style="color: var(--kaauh-teal-dark);"><i class="fas fa-file-alt me-2"></i>Application Instructions</h5><div class="text-secondary">{{ job.application_instructions|safe }}</div></div>{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -123,25 +123,26 @@
|
||||
|
||||
<div class="card mb-4 shadow-sm no-hover">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-muted mb-3" style="font-weight: 500;">Filter & Search</h5>
|
||||
<form method="get" class="row g-3 align-items-end">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<label for="search" class="form-label small text-muted">Search by Title or Department</label>
|
||||
<div class="input-group input-group-lg">
|
||||
<span class="input-group-text bg-white border-end-0"><i class="fas fa-search text-muted"></i></span>
|
||||
<input type="text" name="q" id="search" class="form-control form-control-search border-start-0"
|
||||
placeholder="e.g., 'Professor' or 'Marketing'"
|
||||
value="{{ search_query|default_if_none:'' }}">
|
||||
<div class="input-group input-group-lg mb-3">
|
||||
<form method="get" action="" class="w-100">
|
||||
{% include 'includes/search_form.html' %}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{% url 'job_list' as job_list_url %}
|
||||
|
||||
<form method="GET" class="row g-3 align-items-end" >
|
||||
|
||||
<div class="col-md-3">
|
||||
<label for="status" class="form-label small text-muted">Filter by Status</label>
|
||||
<select name="status" id="status" class="form-select form-select-sm">
|
||||
<option value="">All Statuses</option>
|
||||
<option value="DRAFT" {% if status_filter == 'DRAFT' %}selected{% endif %}>Draft</option>
|
||||
<option value="ACTIVE" {% if status_filter == 'ACTIVE' %}selected{% endif %}>Active</option>
|
||||
<option value="PUBLISHED" {% if status_filter == 'PUBLISHED' %}selected{% endif %}>Published</option>
|
||||
<option value="CLOSED" {% if status_filter == 'CLOSED' %}selected{% endif %}>Closed</option>
|
||||
<option value="ARCHIVED" {% if status_filter == 'ARCHIVED' %}selected{% endif %}>Archived</option>
|
||||
</select>
|
||||
@ -152,17 +153,13 @@
|
||||
<button type="submit" class="btn btn-main-action btn-lg">
|
||||
<i class="fas fa-filter me-1"></i> Apply Filters
|
||||
</button>
|
||||
|
||||
{# Show Clear button if any filter/search is active #}
|
||||
{% if status_filter or search_query %}
|
||||
<a href="{% url 'job_list' %}" class="btn btn-outline-danger btn-sm">
|
||||
<i class="fas fa-times me-1"></i> Clear Filters
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user