Merge branch 'main' of http://10.10.1.136:3000/marwan/kaauh_ats into frontend

This commit is contained in:
Faheed 2025-11-25 15:13:57 +03:00
commit 5b114b630e
21 changed files with 19986 additions and 287 deletions

6
.env
View File

@ -1,3 +1,3 @@
DB_NAME=haikal_db DB_NAME=norahuniversity
DB_USER=faheed DB_USER=norahuniversity
DB_PASSWORD=Faheed@215 DB_PASSWORD=norahuniversity

9882
django.po.bkp Normal file

File diff suppressed because it is too large Load Diff

9885
django2.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@ class ERPIntegrationService:
Validate the incoming request from ERP system Validate the incoming request from ERP system
Returns: (is_valid, error_message) Returns: (is_valid, error_message)
""" """
# Check if source is active # Check if source is active
if not self.source.is_active: if not self.source.is_active:
return False, "Source is not active" return False, "Source is not active"
@ -70,6 +71,7 @@ class ERPIntegrationService:
try: try:
# Map ERP fields to JobPosting fields # Map ERP fields to JobPosting fields
job_data = { job_data = {
'internal_job_id': request_data.get('job_id', '').strip(),
'title': request_data.get('title', '').strip(), 'title': request_data.get('title', '').strip(),
'department': request_data.get('department', '').strip(), 'department': request_data.get('department', '').strip(),
'job_type': self.map_job_type(request_data.get('job_type', 'FULL_TIME')), 'job_type': self.map_job_type(request_data.get('job_type', 'FULL_TIME')),

View File

@ -269,7 +269,7 @@ class SourceAdvancedForm(forms.ModelForm):
class PersonForm(forms.ModelForm): class PersonForm(forms.ModelForm):
class Meta: class Meta:
model = Person model = Person
fields = ["first_name","middle_name", "last_name", "email", "phone","date_of_birth","nationality","address","gender"] fields = ["first_name","middle_name", "last_name", "email", "phone","date_of_birth","nationality","gender","address"]
widgets = { widgets = {
"first_name": forms.TextInput(attrs={'class': 'form-control'}), "first_name": forms.TextInput(attrs={'class': 'form-control'}),
"middle_name": forms.TextInput(attrs={'class': 'form-control'}), "middle_name": forms.TextInput(attrs={'class': 'form-control'}),
@ -591,7 +591,6 @@ class JobPostingForm(forms.ModelForm):
attrs={ attrs={
"class": "form-control", "class": "form-control",
"min": 1, "min": 1,
"placeholder": "Maximum number of applicants",
} }
), ),
} }
@ -834,9 +833,9 @@ class ProfileImageUploadForm(forms.ModelForm):
class StaffUserCreationForm(UserCreationForm): class StaffUserCreationForm(UserCreationForm):
email = forms.EmailField(required=True) email = forms.EmailField(label=_("Email"), required=True)
first_name = forms.CharField(max_length=30, required=True) first_name = forms.CharField(label=_("First Name"),max_length=30, required=True)
last_name = forms.CharField(max_length=150, required=True) last_name = forms.CharField(label=_("Last Name"),max_length=150, required=True)
class Meta: class Meta:
model = User model = User
@ -1078,7 +1077,6 @@ class AgencyJobAssignmentForm(forms.ModelForm):
attrs={ attrs={
"class": "form-control", "class": "form-control",
"min": 1, "min": 1,
"placeholder": "Maximum number of candidates",
} }
), ),
"deadline_date": forms.DateTimeInput( "deadline_date": forms.DateTimeInput(
@ -1090,7 +1088,6 @@ class AgencyJobAssignmentForm(forms.ModelForm):
attrs={ attrs={
"class": "form-control", "class": "form-control",
"rows": 3, "rows": 3,
"placeholder": "Internal notes about this assignment",
} }
), ),
} }

View File

@ -0,0 +1,18 @@
# Generated by Django 5.2.6 on 2025-11-23 12:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('recruitment', '0005_alter_interviewschedule_template_location'),
]
operations = [
migrations.AlterField(
model_name='customuser',
name='email',
field=models.EmailField(error_messages={'unique': 'A user with this email already exists.'}, max_length=254, unique=True),
),
]

View File

@ -47,6 +47,12 @@ class CustomUser(AbstractUser):
designation = models.CharField( designation = models.CharField(
max_length=100, blank=True, null=True, verbose_name=_("Designation") max_length=100, blank=True, null=True, verbose_name=_("Designation")
) )
email = models.EmailField(
unique=True,
error_messages={
"unique": _("A user with this email already exists."),
},
)
class Meta: class Meta:
verbose_name = _("User") verbose_name = _("User")
@ -487,7 +493,6 @@ class Person(Base):
unique=True, unique=True,
db_index=True, db_index=True,
verbose_name=_("Email"), verbose_name=_("Email"),
help_text=_("Unique email address for the person"),
) )
phone = models.CharField( phone = models.CharField(
max_length=20, blank=True, null=True, verbose_name=_("Phone") max_length=20, blank=True, null=True, verbose_name=_("Phone")

View File

@ -187,7 +187,6 @@ class PersonCreateView(CreateView):
template_name = "people/create_person.html" template_name = "people/create_person.html"
form_class = PersonForm form_class = PersonForm
success_url = reverse_lazy("person_list") success_url = reverse_lazy("person_list")
print("from agency")
def form_valid(self, form): def form_valid(self, form):
if "HX-Request" in self.request.headers: if "HX-Request" in self.request.headers:
instance = form.save() instance = form.save()
@ -196,7 +195,6 @@ class PersonCreateView(CreateView):
slug = self.request.POST.get("agency") slug = self.request.POST.get("agency")
if slug: if slug:
agency = HiringAgency.objects.get(slug=slug) agency = HiringAgency.objects.get(slug=slug)
print(agency)
instance.agency = agency instance.agency = agency
instance.save() instance.save()
return redirect("agency_portal_persons_list") return redirect("agency_portal_persons_list")
@ -867,7 +865,10 @@ def kaauh_career(request):
# job detail facing the candidate: # job detail facing the candidate:
def application_detail(request, slug): def application_detail(request, slug):
job = get_object_or_404(JobPosting, slug=slug) job = get_object_or_404(JobPosting, slug=slug)
return render(request, "applicant/application_detail.html", {"job": job}) already_applied = False
if request.user.is_authenticated:
already_applied = Application.objects.filter(job=job,person=request.user.person_profile).exists()
return render(request, "applicant/application_detail.html", {"job": job,"already_applied":already_applied})
@login_required @login_required
@ -1202,7 +1203,13 @@ def application_submit_form(request, template_slug):
"""Display the form as a step-by-step wizard""" """Display the form as a step-by-step wizard"""
if not request.user.is_authenticated: if not request.user.is_authenticated:
return redirect("candidate_signup",slug=template_slug) return redirect("candidate_signup",slug=template_slug)
template = get_object_or_404(FormTemplate, slug=template_slug, is_active=True) template = get_object_or_404(FormTemplate, slug=template_slug, is_active=True)
if Application.objects.filter(job=template.job,person=request.user.person_profile).exists():
messages.error(request, _("You have already submitted an application for this job."))
return redirect("application_detail",slug=template.job.slug)
stage = template.stages.filter(name="Contact Information") stage = template.stages.filter(name="Contact Information")

View File

@ -202,11 +202,14 @@ class ApplicationCreateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessa
job = get_object_or_404(models.JobPosting, slug=self.kwargs['slug']) job = get_object_or_404(models.JobPosting, slug=self.kwargs['slug'])
form.instance.job = job form.instance.job = job
return super().form_valid(form) return super().form_valid(form)
def form_invalid(self, form):
messages.error(self.request, f"{form.errors.as_text()}")
return super().form_invalid(form)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
if self.request.method == 'GET': # if self.request.method == 'GET':
context['person_form'] = forms.PersonForm() context['person_form'] = forms.PersonForm()
return context return context
class ApplicationUpdateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, UpdateView): class ApplicationUpdateView(LoginRequiredMixin, StaffRequiredMixin, SuccessMessageMixin, UpdateView):

View File

@ -81,6 +81,17 @@ class ERPIntegrationView(View):
'message': 'Source not found' 'message': 'Source not found'
}, status=404) }, status=404)
job_id = data.get('job_id')
if not job_id:
return JsonResponse({
'status': 'error',
'message': 'Job ID is required and must be unique'
})
if JobPosting.objects.filter(internal_job_id=job_id).exists():
return JsonResponse({
'status': 'error',
'message': 'Job with this ID already exists'
}, status=400)
# Create integration service # Create integration service
service = ERPIntegrationService(source) service = ERPIntegrationService(source)
@ -144,6 +155,7 @@ class ERPIntegrationView(View):
def _create_job(self, service: ERPIntegrationService, data: Dict[str, Any]) -> tuple[Dict[str, Any], str]: def _create_job(self, service: ERPIntegrationService, data: Dict[str, Any]) -> tuple[Dict[str, Any], str]:
"""Create a new job from ERP data""" """Create a new job from ERP data"""
# Validate ERP data # Validate ERP data
# print(data)
is_valid, error_msg = service.validate_erp_data(data) is_valid, error_msg = service.validate_erp_data(data)
if not is_valid: if not is_valid:
return None, error_msg return None, error_msg
@ -152,7 +164,6 @@ class ERPIntegrationView(View):
job, error_msg = service.create_job_from_erp(data) job, error_msg = service.create_job_from_erp(data)
if error_msg: if error_msg:
return None, error_msg return None, error_msg
# Prepare response data # Prepare response data
response_data = { response_data = {
'job_id': job.internal_job_id, 'job_id': job.internal_job_id,

View File

@ -36,12 +36,22 @@
<p class="text-muted small mb-3">{% trans "Review the full job details below before submitting your application." %}</p> <p class="text-muted small mb-3">{% trans "Review the full job details below before submitting your application." %}</p>
{% if job.form_template %} {% if job.form_template %}
<a href="{% url 'application_submit_form' job.form_template.slug %}" class="btn btn-main-action btn-lg w-100 shadow-sm"> {% if user.is_authenticated and already_applied %}
<button class="btn btn-main-action btn-lg w-100" disabled>
<i class="fas fa-paper-plane me-2"></i> {% trans "You already applied for this position" %}
</button>
{% else %}
<a href="{% url 'application_submit_form' job.form_template.slug %}" class="btn btn-main-action btn-lg w-100">
<i class="fas fa-paper-plane me-2"></i> {% trans "Apply for this Position" %}
</a>
{% endif %}
{% endif %}
{% comment %} <a href="{% url 'application_submit_form' job.form_template.slug %}" class="btn btn-main-action btn-lg w-100 shadow-sm">
<i class="fas fa-paper-plane me-2"></i> {% trans "Apply for this Position" %} <i class="fas fa-paper-plane me-2"></i> {% trans "Apply for this Position" %}
</a> </a>
{% elif not job.is_expired %} {% elif not job.is_expired %}
<p class="text-danger fw-bold">{% trans "Application form is unavailable." %}</p> <p class="text-danger fw-bold">{% trans "Application form is unavailable." %}</p>
{% endif %} {% endif %} {% endcomment %}
</div> </div>
</div> </div>
</div> </div>
@ -196,7 +206,6 @@
{% endwith %} {% endwith %}
</div> </div>
</div> </div>
</article> </article>
</div> </div>
@ -205,11 +214,17 @@
{# 📱 MOBILE FIXED APPLY BAR (Replaced inline style with utility classes) #} {# 📱 MOBILE FIXED APPLY BAR (Replaced inline style with utility classes) #}
{% if job.form_template %} {% if job.form_template %}
<footer class="fixed-bottom d-lg-none bg-white border-top shadow-lg p-3"> <footer class="fixed-bottom d-lg-none bg-white border-top shadow-lg p-3">
<a href="{% url 'application_submit_form' job.form_template.slug %}" class="btn btn-main-action btn-lg w-100"> {% if user.is_authenticated and already_applied %}
<i class="fas fa-paper-plane me-2"></i> {% trans "Apply for this Position" %} <button class="btn btn-main-action btn-lg w-100" disabled>
</a> <i class="fas fa-paper-plane me-2"></i> {% trans "You already applied for this position" %}
</footer> </button>
{% else %}
<a href="{% url 'application_submit_form' job.form_template.slug %}" class="btn btn-main-action btn-lg w-100">
<i class="fas fa-paper-plane me-2"></i> {% trans "Apply for this Position" %}
</a>
{% endif %}
</footer>
{% endif %} {% endif %}

View File

@ -123,14 +123,16 @@
</li> {% endcomment %} </li> {% endcomment %}
<li class="nav-item me-2"> <li class="nav-item me-2">
{% if LANGUAGE_CODE == 'en' %} {% if LANGUAGE_CODE == 'en' %}
<form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %} <form action="{% url 'set_language' %}" method="post" class="d-inline">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.get_full_path }}"> <input name="next" type="hidden" value="{{ request.get_full_path }}">
<button name="language" value="ar" class="btn bg-primary-theme text-white" type="submit"> <button name="language" value="ar" class="btn bg-primary-theme text-white" type="submit">
<span class="me-2">🇸🇦</span> العربية <span class="me-2">🇸🇦</span> العربية
</button> </button>
</form> </form>
{% elif LANGUAGE_CODE == 'ar' %} {% elif LANGUAGE_CODE == 'ar' %}
<form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %} <form action="{% url 'set_language' %}" method="post" class="d-inline">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.get_full_path }}"> <input name="next" type="hidden" value="{{ request.get_full_path }}">
<button name="language" value="en" class="btn bg-primary-theme text-white" type="submit"> <button name="language" value="en" class="btn bg-primary-theme text-white" type="submit">
<span class="me-2">🇺🇸</span> English <span class="me-2">🇺🇸</span> English

View File

@ -1249,7 +1249,7 @@ const elements = {
if (result.success) { if (result.success) {
state.templateId = result.template_slug; state.templateId = result.template_slug;
window.location.href = "{% url 'form_templates_list' %}"; window.location.href = "{% url 'job_detail' template.job.slug %}";
} else { } else {
alert('Error saving form template: ' + result.error); alert('Error saving form template: ' + result.error);

View File

@ -361,7 +361,10 @@
{% trans "Manage the custom application forms associated with this job posting." %} {% trans "Manage the custom application forms associated with this job posting." %}
</p> </p>
{% if not job.form_template %} <a href="{% url 'form_builder' job.form_template.slug %}" class="btn btn-outline-secondary w-100">
<i class="fas fa-list-alt me-1"></i> {% trans "Manage Job Form" %}
</a>
{% comment %} {% if not job.form_template %}
<a href="{% url 'create_form_template' %}" class="btn btn-main-action"> <a href="{% url 'create_form_template' %}" class="btn btn-main-action">
<i class="fas fa-plus-circle me-1"></i> {% trans "Create New Form Template" %} <i class="fas fa-plus-circle me-1"></i> {% trans "Create New Form Template" %}
</a> </a>
@ -377,7 +380,7 @@
<p>{% trans "This job status is not active, the form will appear once the job is made active"%}</p> <p>{% trans "This job status is not active, the form will appear once the job is made active"%}</p>
{% endif %} {% endif %}
{% endif %} {% endif %} {% endcomment %}
</div> </div>
</div> </div>

View File

@ -276,7 +276,8 @@
<th scope="col" rowspan="2">{% trans "Source" %}</th> <th scope="col" rowspan="2">{% trans "Source" %}</th>
<th scope="col" rowspan="2">{% trans "Max Apps" %}</th> <th scope="col" rowspan="2">{% trans "Max Apps" %}</th>
<th scope="col" rowspan="2">{% trans "Deadline" %}</th> <th scope="col" rowspan="2">{% trans "Deadline" %}</th>
<th scope="col" rowspan="2">{% trans "Submission" %}</th> <th scope="col" rowspan="2">{% trans "Assigned To" %}</th>
<th scope="col" rowspan="2"></th>
<th scope="col" colspan="6" class="candidate-management-header-title"> <th scope="col" colspan="6" class="candidate-management-header-title">
@ -306,11 +307,20 @@
<td>{{ job.get_source }}</td> <td>{{ job.get_source }}</td>
<td>{{ job.max_applications }}</td> <td>{{ job.max_applications }}</td>
<td>{{ job.application_deadline|date:"d-m-Y" }}</td> <td>{{ job.application_deadline|date:"d-m-Y" }}</td>
{% if job.assigned_to %}
<td>
<span class="badge bg-primary-theme">
{{ job.assigned_to.first_name|capfirst }} {{ job.assigned_to.last_name|capfirst }}
</span>
</td>
{% else %}
<td>{% trans "Unassigned" %}</td>
{% endif %}
<td> <td>
{% if job.form_template %} {% if job.form_template %}
<div class="btn-group btn-group-sm" role="group"> <div class="btn-group btn-group-sm" role="group">
<a href="{% url 'form_template_submissions_list' job.form_template.slug %}" class="btn btn-outline-secondary" title="{% trans 'All Application Submissions' %}"> <a href="{% url 'form_template_submissions_list' job.form_template.slug %}" class="btn btn-outline-secondary" title="{% trans 'All Application Submissions' %}">
<i class="fas fa-file-alt text-primary-theme"></i> <i class="fas fa-file-alt text-primary-theme"></i>{% trans "Submissions" %}
</a> </a>
</div> </div>

View File

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static i18n crispy_forms_tags %} {% load static i18n crispy_forms_tags %}
{% block title %}Create Person - {{ block.super }}{% endblock %} {% block title %}Create Applicant - {{ block.super }}{% endblock %}
{% block customCSS %} {% block customCSS %}
<style> <style>
@ -184,9 +184,9 @@
<form method="post" enctype="multipart/form-data" id="person-form"> <form method="post" enctype="multipart/form-data" id="person-form">
{% csrf_token %} {% csrf_token %}
{{form|crispy}}
<!-- Profile Image Section --> <!-- Profile Image Section -->
<div class="row mb-4"> {% comment %} <div class="row mb-4">
<div class="col-12"> <div class="col-12">
<div class="profile-image-upload" onclick="document.getElementById('id_profile_image').click()"> <div class="profile-image-upload" onclick="document.getElementById('id_profile_image').click()">
<div id="image-preview-container"> <div id="image-preview-container">
@ -261,7 +261,7 @@
<div class="col-12"> <div class="col-12">
{{ form.address }} {{ form.address }}
</div> </div>
</div> </div> {% endcomment %}
<!-- LinkedIn Profile Section --> <!-- LinkedIn Profile Section -->
{% comment %} <div class="row mb-4"> {% comment %} <div class="row mb-4">
@ -292,11 +292,8 @@
<i class="fas fa-times me-1"></i> {% trans "Cancel" %} <i class="fas fa-times me-1"></i> {% trans "Cancel" %}
</a> </a>
<div class="d-flex gap-2"> <div class="d-flex gap-2">
<button type="reset" class="btn btn-outline-secondary">
<i class="fas fa-undo me-1"></i> {% trans "Reset" %}
</button>
<button type="submit" class="btn btn-main-action"> <button type="submit" class="btn btn-main-action">
<i class="fas fa-save me-1"></i> {% trans "Create Person" %} <i class="fas fa-save me-1"></i> {% trans "Create Applicant" %}
</button> </button>
</div> </div>
</div> </div>
@ -308,141 +305,3 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% block customJS %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Profile Image Preview
const profileImageInput = document.getElementById('id_profile_image');
const imagePreviewContainer = document.getElementById('image-preview-container');
profileImageInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file && file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = function(e) {
imagePreviewContainer.innerHTML = `
<img src="${e.target.result}" alt="Profile Preview" class="profile-image-preview">
<h5 class="text-muted mt-3">${file.name}</h5>
<p class="text-muted small">{% trans "Click to change photo" %}</p>
`;
};
reader.readAsDataURL(file);
}
});
// Form Validation
const form = document.getElementById('person-form');
form.addEventListener('submit', function(e) {
const submitBtn = form.querySelector('button[type="submit"]');
submitBtn.classList.add('loading');
submitBtn.disabled = true;
// Basic validation
const firstName = document.getElementById('id_first_name').value.trim();
const lastName = document.getElementById('id_last_name').value.trim();
const email = document.getElementById('id_email').value.trim();
if (!firstName || !lastName) {
e.preventDefault();
submitBtn.classList.remove('loading');
submitBtn.disabled = false;
alert('{% trans "First name and last name are required." %}');
return;
}
if (email && !isValidEmail(email)) {
e.preventDefault();
submitBtn.classList.remove('loading');
submitBtn.disabled = false;
alert('{% trans "Please enter a valid email address." %}');
return;
}
});
// Email validation helper
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// LinkedIn URL validation
const linkedinInput = document.getElementById('id_linkedin_profile');
linkedinInput.addEventListener('blur', function() {
const value = this.value.trim();
if (value && !isValidLinkedInURL(value)) {
this.classList.add('is-invalid');
if (!this.nextElementSibling || !this.nextElementSibling.classList.contains('invalid-feedback')) {
const feedback = document.createElement('div');
feedback.className = 'invalid-feedback';
feedback.textContent = '{% trans "Please enter a valid LinkedIn URL" %}';
this.parentNode.appendChild(feedback);
}
} else {
this.classList.remove('is-invalid');
const feedback = this.parentNode.querySelector('.invalid-feedback');
if (feedback) feedback.remove();
}
});
function isValidLinkedInURL(url) {
const linkedinRegex = /^https?:\/\/(www\.)?linkedin\.com\/.+/i;
return linkedinRegex.test(url);
}
// Drag and Drop functionality
const uploadArea = document.querySelector('.profile-image-upload');
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
uploadArea.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
uploadArea.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
uploadArea.addEventListener(eventName, unhighlight, false);
});
function highlight(e) {
uploadArea.style.borderColor = 'var(--kaauh-teal)';
uploadArea.style.backgroundColor = 'var(--kaauh-gray-light)';
}
function unhighlight(e) {
uploadArea.style.borderColor = 'var(--kaauh-border)';
uploadArea.style.backgroundColor = 'transparent';
}
uploadArea.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0) {
profileImageInput.files = files;
const event = new Event('change', { bubbles: true });
profileImageInput.dispatchEvent(event);
}
}
});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script>
$(document).ready(function() {
$('.select2').select2();
});
</script>
{% endblock %}

View File

@ -121,7 +121,7 @@
{% comment %} <li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle text-white" href="#" role="button" data-bs-toggle="dropdown" <a class="nav-link dropdown-toggle text-white" href="#" role="button" data-bs-toggle="dropdown"
data-bs-offset="0, 8" aria-expanded="false" aria-label="{% trans 'Toggle language menu' %}"> data-bs-offset="0, 8" aria-expanded="false" aria-label="{% trans 'Toggle language menu' %}">
<i class="fas fa-globe me-1"></i> <i class="fas fa-globe me-1"></i>

View File

@ -104,7 +104,7 @@
<div> <div>
<h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;"> <h1 class="h3 mb-1" style="color: var(--kaauh-teal-dark); font-weight: 700;">
<i class="fas fa-tasks me-2"></i> <i class="fas fa-tasks me-2"></i>
{{ title }} {% trans "Create New Assignment" %}
</h1> </h1>
<p class="text-muted mb-0"> <p class="text-muted mb-0">
{% trans "Assign a job to an external hiring agency" %} {% trans "Assign a job to an external hiring agency" %}
@ -213,7 +213,7 @@
<i class="fas fa-times me-1"></i> {% trans "Cancel" %} <i class="fas fa-times me-1"></i> {% trans "Cancel" %}
</a> </a>
<button type="submit" class="btn btn-main-action"> <button type="submit" class="btn btn-main-action">
<i class="fas fa-save me-1"></i> {{ button_text }} <i class="fas fa-save me-1"></i> {% trans "Create Assignment" %}
</button> </button>
</div> </div>
</form> </form>

View File

@ -279,7 +279,7 @@
<th scope="col" >{% trans "Major" %}</th> <th scope="col" >{% trans "Major" %}</th>
<th scope="col" >{% trans "Stage" %}</th> <th scope="col" >{% trans "Stage" %}</th>
<th scope="col">{% trans "Hiring Source" %}</th> <th scope="col">{% trans "Hiring Source" %}</th>
<th scope="col" >{% trans "created At" %}</th> <th scope="col" >{% trans "Created At" %}</th>
<th scope="col" class="text-end">{% trans "Actions" %}</th> <th scope="col" class="text-end">{% trans "Actions" %}</th>
</tr> </tr>
</thead> </thead>

View File

@ -34,7 +34,7 @@
<div class="form-card"> <div class="form-card">
<h2 id="form-title" class="h3 fw-bold mb-4 text-center"> <h2 id="form-title" class="h3 fw-bold mb-4 text-center">
<i class="fas fa-user-plus me-2 text-accent"></i>{% trans "Create Staff User" %} <i class="fas fa-user-plus me-2 text-accent"></i>{% trans "Create User" %}
</h2> </h2>
{% if messages %} {% if messages %}
@ -66,7 +66,7 @@
--bs-btn-active-border-color: #004d55; --bs-btn-active-border-color: #004d55;
--bs-btn-focus-shadow-rgb: 40, 167, 69; --bs-btn-focus-shadow-rgb: 40, 167, 69;
--bs-btn-color: #ffffff;"> --bs-btn-color: #ffffff;">
<i class="fas fa-save me-2"></i>{% trans "Create Staff User" %} <i class="fas fa-save me-2"></i>{% trans "Create User" %}
</button> </button>
</form> </form>