diff --git a/.env b/.env index b9e2bf0..8d7fbd5 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -DB_NAME=norahuniversity -DB_USER=norahuniversity -DB_PASSWORD=norahuniversity \ No newline at end of file +DB_NAME=haikal_db +DB_USER=faheed +DB_PASSWORD=Faheed@215 \ No newline at end of file diff --git a/NorahUniversity/settings.py b/NorahUniversity/settings.py index 8c006e0..ae1397a 100644 --- a/NorahUniversity/settings.py +++ b/NorahUniversity/settings.py @@ -209,31 +209,31 @@ ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True ACCOUNT_FORMS = {"signup": "recruitment.forms.StaffSignupForm"} -# MAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -# EMAIL_HOST = "10.10.1.110" -# EMAIL_PORT = 2225 +MAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" +EMAIL_HOST = "10.10.1.110" +EMAIL_PORT = 2225 # EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" -EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD", "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI") -EMAIL_HOST = "smtp.mailersend.net" -EMAIL_PORT = 2525 -EMAIL_HOST_USER = "MS_lhygCJ@test-65qngkd8nx3lwr12.mlsender.net" -EMAIL_HOST_PASSWORD = "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI" -EMAIL_USE_TLS = True -EMAIL_HOST = 'sandbox.smtp.mailtrap.io' -EMAIL_HOST_USER = '38e5179debe69a' -EMAIL_HOST_PASSWORD = 'ffa75647d01ecb' -EMAIL_PORT = '2525' +# EMAIL_HOST_PASSWORD = os.getenv("EMAIL_PASSWORD", "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI") +# EMAIL_HOST = "smtp.mailersend.net" +# EMAIL_PORT = 2525 +# EMAIL_HOST_USER = "MS_lhygCJ@test-65qngkd8nx3lwr12.mlsender.net" +# EMAIL_HOST_PASSWORD = "mssp.0Q0rSwb.zr6ke4n2k3e4on12.aHwJqnI" +# EMAIL_USE_TLS = True +# EMAIL_HOST = 'sandbox.smtp.mailtrap.io' +# EMAIL_HOST_USER = '38e5179debe69a' +# EMAIL_HOST_PASSWORD = 'ffa75647d01ecb' +# EMAIL_PORT = '2525' -EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_HOST = 'smtp.gmail.com' -EMAIL_PORT = 587 -EMAIL_USE_TLS = True -EMAIL_HOST_USER = 'faheedk215@gmail.com' # Use your actual Gmail email address -EMAIL_HOST_PASSWORD = 'nfxf xpzo bpsb lqje' # -DEFAULT_FROM_EMAIL='faheedlearn@gmail.com' +# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +# EMAIL_HOST = 'smtp.gmail.com' +# EMAIL_PORT = 587 +# EMAIL_USE_TLS = True +# EMAIL_HOST_USER = 'faheedk215@gmail.com' # Use your actual Gmail email address +# EMAIL_HOST_PASSWORD = 'nfxf xpzo bpsb lqje' # +# DEFAULT_FROM_EMAIL='faheedlearn@gmail.com' # Crispy Forms Configuration CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" diff --git a/recruitment/views.py b/recruitment/views.py index 353423f..8bafd63 100644 --- a/recruitment/views.py +++ b/recruitment/views.py @@ -215,7 +215,7 @@ class PersonListView(StaffRequiredMixin, ListView, LoginRequiredMixin): .distinct() .order_by("nationality") ) - + nationality = self.request.GET.get("nationality") context["nationality"] = nationality context["nationalities"] = nationalities @@ -1308,8 +1308,9 @@ def delete_form_template(request, template_id): def application_submit_form(request, slug): """Display the form as a step-by-step wizard""" job = get_object_or_404(JobPosting, slug=slug) + template_slug=job.form_template.slug if not request.user.is_authenticated: - return redirect("application_signup", slug=slug) + return redirect("application_signup", slug=template_slug) if request.user.user_type == "candidate": person = request.user.person_profile @@ -2432,8 +2433,11 @@ def create_staff_user(request): @superuser_required def admin_settings(request): staffs = User.objects.filter(user_type="staff", is_superuser=False) + paginator=Paginator(staffs,20) + page_number=request.GET.get('page') + page_obj=paginator.get_page(page_number) form = ToggleAccountForm() - context = {"staffs": staffs, "form": form} + context = {"staffs": page_obj, "form": form,"page_obj":page_obj} return render(request, "user/admin_settings.html", context) @@ -2769,7 +2773,7 @@ def agency_assignment_list(request): assignments = assignments.filter(status=status_filter) # Pagination - paginator = Paginator(assignments, 15) # Show 15 assignments per page + paginator = Paginator(assignments, 20) # Show 15 assignments per page page_number = request.GET.get("page") page_obj = paginator.get_page(page_number) @@ -4514,133 +4518,133 @@ def api_application_detail(request, candidate_id): # Source CRUD Views -@login_required -@staff_user_required -def source_list(request): - """List all sources with search and pagination""" - search_query = request.GET.get("q", "") - sources = Source.objects.all() +# @login_required +# @staff_user_required +# def source_list(request): +# """List all sources with search and pagination""" +# search_query = request.GET.get("q", "") +# sources = Source.objects.all() + +# if search_query: +# sources = sources.filter( +# Q(name__icontains=search_query) +# | Q(source_type__icontains=search_query) +# | Q(description__icontains=search_query) +# ) - if search_query: - sources = sources.filter( - Q(name__icontains=search_query) - | Q(source_type__icontains=search_query) - | Q(description__icontains=search_query) - ) +# # Order by most recently created +# sources = sources.order_by("-created_at") - # Order by most recently created - sources = sources.order_by("-created_at") +# # Pagination +# paginator = Paginator(sources, 1) # Show 15 sources per page +# page_number = request.GET.get("page") +# page_obj = paginator.get_page(page_number) - # Pagination - paginator = Paginator(sources, 1) # Show 15 sources per page - page_number = request.GET.get("page") - page_obj = paginator.get_page(page_number) - - context = { - "page_obj": page_obj, - "search_query": search_query, - "total_sources": sources.count(), - } - return render(request, "recruitment/source_list.html", context) +# context = { +# "page_obj": page_obj, +# "search_query": search_query, +# "total_sources": sources.count(), +# } +# return render(request, "recruitment/source_list.html", context) -@login_required -@staff_user_required -def source_create(request): - """Create a new source""" - if request.method == "POST": - form = SourceForm(request.POST) - if form.is_valid(): - source = form.save() - messages.success(request, f'Source "{source.name}" created successfully!') - return redirect("source_detail", slug=source.slug) - else: - messages.error(request, "Please correct the errors below.") - else: - form = SourceForm() +# @login_required +# @staff_user_required +# def source_create(request): +# """Create a new source""" +# if request.method == "POST": +# form = SourceForm(request.POST) +# if form.is_valid(): +# source = form.save() +# messages.success(request, f'Source "{source.name}" created successfully!') +# return redirect("source_detail", slug=source.slug) +# else: +# messages.error(request, "Please correct the errors below.") +# else: +# form = SourceForm() - context = { - "form": form, - "title": "Create New Source", - "button_text": "Create Source", - } - return render(request, "recruitment/source_form.html", context) +# context = { +# "form": form, +# "title": "Create New Source", +# "button_text": "Create Source", +# } +# return render(request, "recruitment/source_form.html", context) -@login_required -@staff_user_required -def source_detail(request, slug): - """View details of a specific source""" - source = get_object_or_404(Source, slug=slug) +# @login_required +# @staff_user_required +# def source_detail(request, slug): +# """View details of a specific source""" +# source = get_object_or_404(Source, slug=slug) - # Get integration logs for this source - integration_logs = source.integration_logs.order_by("-created_at")[ - :10 - ] # Show recent 10 logs +# # Get integration logs for this source +# integration_logs = source.integration_logs.order_by("-created_at")[ +# :10 +# ] # Show recent 10 logs - # Statistics - total_logs = source.integration_logs.count() - successful_logs = source.integration_logs.filter(method="POST").count() - failed_logs = source.integration_logs.filter( - method="POST", status_code__gte=400 - ).count() +# # Statistics +# total_logs = source.integration_logs.count() +# successful_logs = source.integration_logs.filter(method="POST").count() +# failed_logs = source.integration_logs.filter( +# method="POST", status_code__gte=400 +# ).count() - context = { - "source": source, - "integration_logs": integration_logs, - "total_logs": total_logs, - "successful_logs": successful_logs, - "failed_logs": failed_logs, - } - return render(request, "recruitment/source_detail.html", context) +# context = { +# "source": source, +# "integration_logs": integration_logs, +# "total_logs": total_logs, +# "successful_logs": successful_logs, +# "failed_logs": failed_logs, +# } +# return render(request, "recruitment/source_detail.html", context) -@login_required -@staff_user_required -def source_update(request, slug): - """Update an existing source""" - source = get_object_or_404(Source, slug=slug) +# @login_required +# @staff_user_required +# def source_update(request, slug): +# """Update an existing source""" +# source = get_object_or_404(Source, slug=slug) - if request.method == "POST": - form = SourceForm(request.POST, instance=source) - if form.is_valid(): - source = form.save() - messages.success(request, f'Source "{source.name}" updated successfully!') - return redirect("source_detail", slug=source.slug) - else: - messages.error(request, "Please correct the errors below.") - else: - form = SourceForm(instance=source) +# if request.method == "POST": +# form = SourceForm(request.POST, instance=source) +# if form.is_valid(): +# source = form.save() +# messages.success(request, f'Source "{source.name}" updated successfully!') +# return redirect("source_detail", slug=source.slug) +# else: +# messages.error(request, "Please correct the errors below.") +# else: +# form = SourceForm(instance=source) - context = { - "form": form, - "source": source, - "title": _("Edit Source: %(name)s") % {"name": source.name}, - "button_text": _("Update Source"), - } - return render(request, "recruitment/source_form.html", context) +# context = { +# "form": form, +# "source": source, +# "title": _("Edit Source: %(name)s") % {"name": source.name}, +# "button_text": _("Update Source"), +# } +# return render(request, "recruitment/source_form.html", context) -@login_required -@staff_user_required -def source_delete(request, slug): - """Delete a source""" - source = get_object_or_404(Source, slug=slug) +# @login_required +# @staff_user_required +# def source_delete(request, slug): +# """Delete a source""" +# source = get_object_or_404(Source, slug=slug) - if request.method == "POST": - source_name = source.name - source.delete() - messages.success(request, f'Source "{source_name}" deleted successfully!') - return redirect("source_list") +# if request.method == "POST": +# source_name = source.name +# source.delete() +# messages.success(request, f'Source "{source_name}" deleted successfully!') +# return redirect("source_list") - context = { - "source": source, - "title": _("Delete Source: %(name)s") % {"name": source.name}, - "message": _('Are you sure you want to delete the source "%(name)s"?') - % {"name": source.name}, - "cancel_url": reverse("source_detail", kwargs={"slug": source.slug}), - } - return render(request, "recruitment/source_confirm_delete.html", context) +# context = { +# "source": source, +# "title": _("Delete Source: %(name)s") % {"name": source.name}, +# "message": _('Are you sure you want to delete the source "%(name)s"?') +# % {"name": source.name}, +# "cancel_url": reverse("source_detail", kwargs={"slug": source.slug}), +# } +# return render(request, "recruitment/source_confirm_delete.html", context) @login_required @@ -5379,7 +5383,7 @@ class JobListView(LoginRequiredMixin, StaffRequiredMixin, ListView): model = JobPosting template_name = "jobs/job_list.html" context_object_name = "jobs" - paginate_by = 10 + paginate_by = 20 def get_queryset(self): queryset = super().get_queryset().order_by("-created_at") @@ -5507,8 +5511,8 @@ class ApplicationListView(LoginRequiredMixin, StaffRequiredMixin, ListView): queryset = queryset.filter(job__slug=job) if stage: queryset = queryset.filter(stage=stage) - - return queryset.order_by("-created_at") + queryset=queryset.order_by("-created_at") + return queryset def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) diff --git a/static/css/main.css b/static/css/main.css index a7120d8..211e35c 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -16,6 +16,7 @@ --kaauh-info: #17a2b8; --kaauh-danger: #dc3545; --kaauh-warning: #ffc107; + --kaauh-border: #eaeff3; } /* Primary Color Overrides */ @@ -733,5 +734,47 @@ html[dir="rtl"] .me-auto { margin-right: 0 !important; margin-left: auto !import color: var(--kaauh-teal); } + /* --- Pagination Base Styles --- */ + .pagination .page-link { + color: var(--kaauh-teal); /* Teal text for links */ + border-color: var(--kaauh-border); /* Light border */ + background-color: #fff; + font-weight: 500; + font-size: 0.9rem; + transition: all 0.2s ease-in-out; + } - \ No newline at end of file + /* --- Hover State --- */ + .pagination .page-link:hover { + color: var(--kaauh-teal-dark); /* Darker teal text on hover */ + background-color: var(--kaauh-gray-light); + border-color: var(--kaauh-border); + z-index: 2; + } + + /* --- Active State (Current Page) --- */ + /* This targets the
  • and overrides the inner span */ + .pagination .page-item.active .page-link { + background-color: var(--kaauh-teal); + border-color: var(--kaauh-teal); + color: #ffffff; + z-index: 3; + } + + /* --- Focus State (Accessibility) --- */ + /* Replaces the default blue glow with a teal glow */ + .pagination .page-link:focus { + box-shadow: 0 0 0 0.25rem rgba(0, 99, 110, 0.25); + background-color: var(--kaauh-gray-light); + color: var(--kaauh-teal-dark); + } + + /* --- First/Last Item Border Radius (Optional Rounding) --- */ + .pagination .page-item:first-child .page-link { + border-top-left-radius: 0.5rem; + border-bottom-left-radius: 0.5rem; + } + .pagination .page-item:last-child .page-link { + border-top-right-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; + } \ No newline at end of file diff --git a/templates/includes/paginator.html b/templates/includes/paginator.html index d11e555..1923f8d 100644 --- a/templates/includes/paginator.html +++ b/templates/includes/paginator.html @@ -1,60 +1,58 @@ -{% if page_obj.has_previous or page_obj.has_next %} + +{% load url_extras i18n %} +{% if page_obj.has_other_pages %} diff --git a/templates/interviews/interview_list.html b/templates/interviews/interview_list.html index 1b54cec..34ff2e5 100644 --- a/templates/interviews/interview_list.html +++ b/templates/interviews/interview_list.html @@ -138,19 +138,7 @@ font-size: 0.8rem !important; } - /* Pagination Styling */ - .pagination .page-link { - color: var(--kaauh-teal); - border-color: var(--kaauh-border); - } - .pagination .page-item.active .page-link { - background-color: var(--kaauh-teal); - border-color: var(--kaauh-teal); - } - .pagination .page-link:hover { - color: var(--kaauh-teal-dark); - background-color: #f8f9fa; - } + {% endblock %} diff --git a/templates/jobs/job_applicants.html b/templates/jobs/job_applicants.html index ab66e5a..4b8abfa 100644 --- a/templates/jobs/job_applicants.html +++ b/templates/jobs/job_applicants.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% load static %} - +{% load i18n %} {% block title %}{{ job.title }} - Applicants{% endblock %} {% block customCSS %} @@ -374,33 +374,31 @@ {% block content %}
    -

    {{ job.title }}

    {{ job.department|default:"General" }} โ€ข {{ job.get_job_type_display }} โ€ข {{ job.get_workplace_type_display }}

    - - {{ total_applications }} Total Applicants + + {{ total_applications }} {% trans "Total Applicants" %}
    {% if job.max_applications %}
    - - {{ job.max_applications }} Positions Available + + {{ job.max_applications }} {% trans "Positions Available" %}
    {% endif %}
    - - Posted {{ job.created_at|date:"M d, Y" }} + + {% trans "Posted" %} {{ job.created_at|date:"M d, Y" }}
    -
    {{ total_applications }}
    -
    Total Applications
    +
    {% trans "Total Applications" %}
    {% for stage_key, stage_data in stage_stats.items %} @@ -413,32 +411,29 @@ {% if ai_score_stats.average %}
    {{ ai_score_stats.average|floatformat:1 }}
    -
    Average AI Score
    +
    {% trans "Average AI Score" %}
    {% endif %}
    -
    -
    - +
    -
    - +
    -
    - +
    - +
    -
    - +
    - +
    -
    - +
    -
    - Clear All + {% trans "Clear All" %}
    - {% if page_obj.object_list %}
    {% for application in page_obj.object_list %}
    -
    @@ -541,15 +530,14 @@
    -
    - - Applied {{ application.created_at|date:"M d, Y" }} + + {% trans "Applied" %} {{ application.created_at|date:"M d, Y" }}
    {% if application.ai_analysis_data.analysis_data_en.match_score %}
    - + {{ application.ai_analysis_data.analysis_data_en.match_score }}% @@ -557,66 +545,46 @@ {% endif %} {% if application.person.gpa %}
    - - GPA: {{ application.person.gpa|floatformat:2 }} + + {% trans "GPA" %}: {{ application.person.gpa|floatformat:2 }}
    {% endif %}
    -
    {% endfor %}
    - {% if page_obj.has_other_pages %} - + {% include "includes/paginator.html" %} {% endif %} {% else %} -
    -

    ๐Ÿ˜” No Applicants Found

    +

    ๐Ÿ˜” {% trans "No Applicants Found" %}

    {% if search_query or stage_filter or min_ai_score or max_ai_score or date_from or date_to %} - We couldn't find any applicants matching your current filters. Try adjusting your search criteria or clearing some filters. + {% trans "We couldn't find any applicants matching your current filters. Try adjusting your search criteria or clearing some filters." %} {% else %} - There are currently no applicants for this job. + {% trans "There are currently no applicants for this job." %} {% endif %}

    - ๐Ÿ”„ Clear Filters + {% trans "Clear Filters" %}
    {% endif %}
    -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/templates/jobs/job_bank.html b/templates/jobs/job_bank.html index e356f6c..cea8783 100644 --- a/templates/jobs/job_bank.html +++ b/templates/jobs/job_bank.html @@ -1,5 +1,6 @@ {% extends "base.html" %} {% load static %} +{% load i18n %} {% block title %}Job Bank - All Opportunities{% endblock %} @@ -303,36 +304,7 @@ color: white; } -.pagination { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; - margin-top: 2rem; -} -.pagination a, -.pagination span { - padding: 0.75rem 1rem; - border: 2px solid #e1e5e9; - border-radius: 8px; - text-decoration: none; - color: #333; - font-weight: 500; - transition: all 0.3s ease; -} - -.pagination a:hover { - background: #667eea; - color: white; - border-color: #667eea; -} - -.pagination .current { - background: #667eea; - color: white; - border-color: #667eea; -} .no-results { text-align: center; @@ -387,8 +359,8 @@
    -

    ๐Ÿฆ Job Bank

    -

    Explore all available opportunities across departments and find your perfect role

    +

    ๐Ÿฆ {% trans "Job Bank" %}

    +

    {% trans "Explore all available opportunities across departments and find your perfect role" %}

    @@ -397,21 +369,21 @@
    - + - + {% for key, value in job_types.items %}
    @@ -549,11 +521,11 @@
    @@ -562,35 +534,21 @@ {% if page_obj.has_other_pages %} - + {% include "includes/paginator.html" %} {% endif %} {% else %}
    -

    ๐Ÿ˜” No Jobs Found

    +

    {% trans "No Jobs Found" %}

    {% if search_query or department_filter or job_type_filter or workplace_type_filter or status_filter or date_filter %} - We couldn't find any jobs matching your current filters. Try adjusting your search criteria or clearing some filters. + {% trans "We couldn't find any jobs matching your current filters. Try adjusting your search criteria or clearing some filters." %} {% else %} - There are currently no job postings in the system. Check back later for new opportunities! + {% trans "There are currently no job postings in the system. Check back later for new opportunities!" %} {% endif %}

    - ๐Ÿ”„ Clear Filters + {% trans "Clear Filters" %}
    {% endif %} diff --git a/templates/messages/message_list.html b/templates/messages/message_list.html index 4f52188..653891c 100644 --- a/templates/messages/message_list.html +++ b/templates/messages/message_list.html @@ -182,39 +182,8 @@
    - {% if page_obj.has_other_pages %} - - {% endif %} + + {% include "includes/paginator.html" %} {% else %}
    diff --git a/templates/people/person_list.html b/templates/people/person_list.html index 8659c96..607a3c4 100644 --- a/templates/people/person_list.html +++ b/templates/people/person_list.html @@ -108,19 +108,7 @@ background-color: var(--kaauh-gray-light); } - /* Pagination Link Styling */ - .pagination .page-item .page-link { - color: var(--kaauh-teal-dark); - border-color: var(--kaauh-border); - } - .pagination .page-item.active .page-link { - background-color: var(--kaauh-teal); - border-color: var(--kaauh-teal); - color: white; - } - .pagination .page-item:hover .page-link:not(.active) { - background-color: #e9ecef; - } + /* Profile Image Styling */ .profile-image-small { diff --git a/templates/recruitment/agency_assignment_list.html b/templates/recruitment/agency_assignment_list.html index 649a87d..f4c626f 100644 --- a/templates/recruitment/agency_assignment_list.html +++ b/templates/recruitment/agency_assignment_list.html @@ -192,47 +192,7 @@ {% if page_obj.has_other_pages %} - + {% include "includes/paginator.html" %} {% endif %} {% else %}
    diff --git a/templates/recruitment/agency_portal_dashboard.html b/templates/recruitment/agency_portal_dashboard.html index 21d90de..d206895 100644 --- a/templates/recruitment/agency_portal_dashboard.html +++ b/templates/recruitment/agency_portal_dashboard.html @@ -219,6 +219,7 @@
    {% endfor %}
    + {% include "includes/paginator.html" %} {% else %}
    diff --git a/templates/recruitment/applications_list.html b/templates/recruitment/applications_list.html index c7b99aa..78f7a05 100644 --- a/templates/recruitment/applications_list.html +++ b/templates/recruitment/applications_list.html @@ -120,20 +120,7 @@ background-color: var(--kaauh-gray-light); } - /* Pagination Link Styling (Consistent) */ - .pagination .page-item .page-link { - color: var(--kaauh-teal-dark); - border-color: var(--kaauh-border); - } - .pagination .page-item.active .page-link { - background-color: var(--kaauh-teal); - border-color: var(--kaauh-teal); - color: white; - } - .pagination .page-item:hover .page-link:not(.active) { - background-color: #e9ecef; - } - + /* Filter & Search Layout Adjustments */ .filter-buttons { display: flex; diff --git a/templates/recruitment/partials/stats_cards.html b/templates/recruitment/partials/stats_cards.html index 96551e7..0e945ea 100644 --- a/templates/recruitment/partials/stats_cards.html +++ b/templates/recruitment/partials/stats_cards.html @@ -40,14 +40,14 @@
    {% trans "Total Slots to be Filled " %}
    - {# GLOBAL - 5. Total Participants #} + {% comment %} {# GLOBAL - 5. Total Participants #}

    {% trans "Total Participants" %}

    {{ total_participants }}
    {% trans "Total Recruiters/Interviewers" %}
    -
    +
    {% endcomment %} {# GLOBAL - 6. Total LinkedIn Posts #} {% comment %}