message on exceeding the applications
This commit is contained in:
parent
a3277c53a7
commit
2f02f10c16
Binary file not shown.
Binary file not shown.
@ -132,16 +132,24 @@ WSGI_APPLICATION = 'NorahUniversity.wsgi.application'
|
|||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
||||||
|
|
||||||
|
# DATABASES = {
|
||||||
|
# 'default': {
|
||||||
|
# 'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||||
|
# 'NAME': 'norahuniversity',
|
||||||
|
# 'USER': 'norahuniversity',
|
||||||
|
# 'PASSWORD': 'norahuniversity',
|
||||||
|
# 'HOST': '127.0.0.1',
|
||||||
|
# 'PORT': '5432',
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': 'norahuniversity',
|
'NAME': BASE_DIR / 'db.sqlite3',
|
||||||
'USER': 'norahuniversity',
|
|
||||||
'PASSWORD': 'norahuniversity',
|
|
||||||
'HOST': '127.0.0.1',
|
|
||||||
'PORT': '5432',
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
||||||
|
|
||||||
|
|||||||
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.
@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 5.2.7 on 2025-10-17 19:41
|
# Generated by Django 5.2.7 on 2025-10-19 15:50
|
||||||
|
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
@ -99,8 +99,8 @@ class Migration(migrations.Migration):
|
|||||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='Updated 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')),
|
('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')),
|
||||||
('topic', models.CharField(max_length=255, verbose_name='Topic')),
|
('topic', models.CharField(max_length=255, verbose_name='Topic')),
|
||||||
('meeting_id', models.CharField(max_length=20, unique=True, verbose_name='Meeting ID')),
|
('meeting_id', models.CharField(db_index=True, max_length=20, unique=True, verbose_name='Meeting ID')),
|
||||||
('start_time', models.DateTimeField(verbose_name='Start Time')),
|
('start_time', models.DateTimeField(db_index=True, verbose_name='Start Time')),
|
||||||
('duration', models.PositiveIntegerField(verbose_name='Duration')),
|
('duration', models.PositiveIntegerField(verbose_name='Duration')),
|
||||||
('timezone', models.CharField(max_length=50, verbose_name='Timezone')),
|
('timezone', models.CharField(max_length=50, verbose_name='Timezone')),
|
||||||
('join_url', models.URLField(verbose_name='Join URL')),
|
('join_url', models.URLField(verbose_name='Join URL')),
|
||||||
@ -110,7 +110,7 @@ class Migration(migrations.Migration):
|
|||||||
('mute_upon_entry', models.BooleanField(default=False, verbose_name='Mute Upon Entry')),
|
('mute_upon_entry', models.BooleanField(default=False, verbose_name='Mute Upon Entry')),
|
||||||
('waiting_room', models.BooleanField(default=False, verbose_name='Waiting Room')),
|
('waiting_room', models.BooleanField(default=False, verbose_name='Waiting Room')),
|
||||||
('zoom_gateway_response', models.JSONField(blank=True, null=True, verbose_name='Zoom Gateway Response')),
|
('zoom_gateway_response', models.JSONField(blank=True, null=True, verbose_name='Zoom Gateway Response')),
|
||||||
('status', models.CharField(blank=True, max_length=20, null=True, verbose_name='Status')),
|
('status', models.CharField(blank=True, db_index=True, default='waiting', max_length=20, null=True, verbose_name='Status')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
@ -142,41 +142,6 @@ class Migration(migrations.Migration):
|
|||||||
'ordering': ['order'],
|
'ordering': ['order'],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
|
||||||
name='FormSubmission',
|
|
||||||
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')),
|
|
||||||
('submitted_at', models.DateTimeField(auto_now_add=True)),
|
|
||||||
('applicant_name', models.CharField(blank=True, help_text='Name of the applicant', max_length=200)),
|
|
||||||
('applicant_email', models.EmailField(blank=True, help_text='Email of the applicant', max_length=254)),
|
|
||||||
('submitted_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='form_submissions', to=settings.AUTH_USER_MODEL)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Form Submission',
|
|
||||||
'verbose_name_plural': 'Form Submissions',
|
|
||||||
'ordering': ['-submitted_at'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='FieldResponse',
|
|
||||||
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')),
|
|
||||||
('value', models.JSONField(blank=True, help_text='Response value (stored as JSON)', null=True)),
|
|
||||||
('uploaded_file', models.FileField(blank=True, null=True, upload_to='form_uploads/')),
|
|
||||||
('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='recruitment.formfield')),
|
|
||||||
('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='recruitment.formsubmission')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Field Response',
|
|
||||||
'verbose_name_plural': 'Field Responses',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='FormTemplate',
|
name='FormTemplate',
|
||||||
fields=[
|
fields=[
|
||||||
@ -195,10 +160,24 @@ class Migration(migrations.Migration):
|
|||||||
'ordering': ['-created_at'],
|
'ordering': ['-created_at'],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.CreateModel(
|
||||||
model_name='formsubmission',
|
name='FormSubmission',
|
||||||
name='template',
|
fields=[
|
||||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submissions', to='recruitment.formtemplate'),
|
('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')),
|
||||||
|
('submitted_at', models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||||
|
('applicant_name', models.CharField(blank=True, help_text='Name of the applicant', max_length=200)),
|
||||||
|
('applicant_email', models.EmailField(blank=True, db_index=True, help_text='Email of the applicant', max_length=254)),
|
||||||
|
('submitted_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='form_submissions', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('template', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submissions', to='recruitment.formtemplate')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Form Submission',
|
||||||
|
'verbose_name_plural': 'Form Submissions',
|
||||||
|
'ordering': ['-submitted_at'],
|
||||||
|
},
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='formstage',
|
model_name='formstage',
|
||||||
@ -214,7 +193,7 @@ class Migration(migrations.Migration):
|
|||||||
('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')),
|
('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')),
|
||||||
('first_name', models.CharField(max_length=255, verbose_name='First Name')),
|
('first_name', models.CharField(max_length=255, verbose_name='First Name')),
|
||||||
('last_name', models.CharField(max_length=255, verbose_name='Last Name')),
|
('last_name', models.CharField(max_length=255, verbose_name='Last Name')),
|
||||||
('email', models.EmailField(max_length=254, verbose_name='Email')),
|
('email', models.EmailField(db_index=True, max_length=254, verbose_name='Email')),
|
||||||
('phone', models.CharField(max_length=20, verbose_name='Phone')),
|
('phone', models.CharField(max_length=20, verbose_name='Phone')),
|
||||||
('address', models.TextField(max_length=200, verbose_name='Address')),
|
('address', models.TextField(max_length=200, verbose_name='Address')),
|
||||||
('resume', models.FileField(upload_to='resumes/', verbose_name='Resume')),
|
('resume', models.FileField(upload_to='resumes/', verbose_name='Resume')),
|
||||||
@ -222,19 +201,16 @@ class Migration(migrations.Migration):
|
|||||||
('is_potential_candidate', models.BooleanField(default=False, verbose_name='Potential Candidate')),
|
('is_potential_candidate', models.BooleanField(default=False, verbose_name='Potential Candidate')),
|
||||||
('parsed_summary', models.TextField(blank=True, verbose_name='Parsed Summary')),
|
('parsed_summary', models.TextField(blank=True, verbose_name='Parsed Summary')),
|
||||||
('applied', models.BooleanField(default=False, verbose_name='Applied')),
|
('applied', models.BooleanField(default=False, verbose_name='Applied')),
|
||||||
('stage', models.CharField(choices=[('Applied', 'Applied'), ('Exam', 'Exam'), ('Interview', 'Interview'), ('Offer', 'Offer')], default='Applied', max_length=100, verbose_name='Stage')),
|
('stage', models.CharField(choices=[('Applied', 'Applied'), ('Exam', 'Exam'), ('Interview', 'Interview'), ('Offer', 'Offer')], db_index=True, default='Applied', max_length=100, verbose_name='Stage')),
|
||||||
('applicant_status', models.CharField(blank=True, choices=[('Applicant', 'Applicant'), ('Candidate', 'Candidate')], default='Applicant', max_length=100, null=True, verbose_name='Applicant Status')),
|
('applicant_status', models.CharField(blank=True, choices=[('Applicant', 'Applicant'), ('Candidate', 'Candidate')], default='Applicant', max_length=100, null=True, verbose_name='Applicant Status')),
|
||||||
('exam_date', models.DateTimeField(blank=True, null=True, verbose_name='Exam Date')),
|
('exam_date', models.DateTimeField(blank=True, null=True, verbose_name='Exam Date')),
|
||||||
('exam_status', models.CharField(blank=True, choices=[('Passed', 'Passed'), ('Failed', 'Failed')], max_length=100, null=True, verbose_name='Exam Status')),
|
('exam_status', models.CharField(blank=True, choices=[('Passed', 'Passed'), ('Failed', 'Failed')], max_length=100, null=True, verbose_name='Exam Status')),
|
||||||
('interview_date', models.DateTimeField(blank=True, null=True, verbose_name='Interview Date')),
|
('interview_date', models.DateTimeField(blank=True, null=True, verbose_name='Interview Date')),
|
||||||
('interview_status', models.CharField(blank=True, choices=[('Accepted', 'Accepted'), ('Rejected', 'Rejected')], max_length=100, null=True, verbose_name='Interview Status')),
|
('interview_status', models.CharField(blank=True, choices=[('Passed', 'Passed'), ('Failed', 'Failed')], max_length=100, null=True, verbose_name='Interview Status')),
|
||||||
('offer_date', models.DateField(blank=True, null=True, verbose_name='Offer Date')),
|
('offer_date', models.DateField(blank=True, null=True, verbose_name='Offer Date')),
|
||||||
('offer_status', models.CharField(blank=True, choices=[('Accepted', 'Accepted'), ('Rejected', 'Rejected')], max_length=100, null=True, verbose_name='Offer Status')),
|
('offer_status', models.CharField(blank=True, choices=[('Accepted', 'Accepted'), ('Rejected', 'Rejected')], max_length=100, null=True, verbose_name='Offer Status')),
|
||||||
('join_date', models.DateField(blank=True, null=True, verbose_name='Join Date')),
|
('join_date', models.DateField(blank=True, null=True, verbose_name='Join Date')),
|
||||||
('match_score', models.IntegerField(blank=True, null=True)),
|
('ai_analysis_data', models.JSONField(default=dict, help_text='Full JSON output from the resume scoring model.', verbose_name='AI Analysis Data')),
|
||||||
('strengths', models.TextField(blank=True)),
|
|
||||||
('weaknesses', models.TextField(blank=True)),
|
|
||||||
('criteria_checklist', models.JSONField(blank=True, default=dict)),
|
|
||||||
('submitted_by_agency', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='submitted_candidates', to='recruitment.hiringagency', verbose_name='Submitted by Agency')),
|
('submitted_by_agency', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='submitted_candidates', to='recruitment.hiringagency', verbose_name='Submitted by Agency')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
@ -261,22 +237,23 @@ class Migration(migrations.Migration):
|
|||||||
('benefits', django_ckeditor_5.fields.CKEditor5Field(blank=True, null=True)),
|
('benefits', django_ckeditor_5.fields.CKEditor5Field(blank=True, null=True)),
|
||||||
('application_url', models.URLField(blank=True, help_text='URL where candidates apply', null=True, validators=[django.core.validators.URLValidator()])),
|
('application_url', models.URLField(blank=True, help_text='URL where candidates apply', null=True, validators=[django.core.validators.URLValidator()])),
|
||||||
('application_start_date', models.DateField(blank=True, null=True)),
|
('application_start_date', models.DateField(blank=True, null=True)),
|
||||||
('application_deadline', models.DateField(blank=True, null=True)),
|
('application_deadline', models.DateField(blank=True, db_index=True, null=True)),
|
||||||
('application_instructions', django_ckeditor_5.fields.CKEditor5Field(blank=True, null=True)),
|
('application_instructions', django_ckeditor_5.fields.CKEditor5Field(blank=True, null=True)),
|
||||||
('internal_job_id', models.CharField(editable=False, max_length=50, primary_key=True, serialize=False)),
|
('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)),
|
('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')], default='DRAFT', max_length=20)),
|
('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])),
|
('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])),
|
||||||
('linkedin_post_id', models.CharField(blank=True, help_text='LinkedIn post ID after posting', max_length=200)),
|
('linkedin_post_id', models.CharField(blank=True, help_text='LinkedIn post ID after posting', max_length=200)),
|
||||||
('linkedin_post_url', models.URLField(blank=True, help_text='Direct URL to LinkedIn post')),
|
('linkedin_post_url', models.URLField(blank=True, help_text='Direct URL to LinkedIn post')),
|
||||||
('posted_to_linkedin', models.BooleanField(default=False)),
|
('posted_to_linkedin', models.BooleanField(default=False)),
|
||||||
('linkedin_post_status', models.CharField(blank=True, help_text='Status of LinkedIn posting', max_length=50)),
|
('linkedin_post_status', models.CharField(blank=True, help_text='Status of LinkedIn posting', max_length=50)),
|
||||||
('linkedin_posted_at', models.DateTimeField(blank=True, null=True)),
|
('linkedin_posted_at', models.DateTimeField(blank=True, null=True)),
|
||||||
('published_at', models.DateTimeField(blank=True, null=True)),
|
('published_at', models.DateTimeField(blank=True, db_index=True, null=True)),
|
||||||
('position_number', models.CharField(blank=True, help_text='University position number', max_length=50)),
|
('position_number', models.CharField(blank=True, help_text='University position number', max_length=50)),
|
||||||
('reporting_to', models.CharField(blank=True, help_text='Who this position reports to', max_length=100)),
|
('reporting_to', models.CharField(blank=True, help_text='Who this position reports to', max_length=100)),
|
||||||
('joining_date', models.DateField(blank=True, help_text='Desired start date', null=True)),
|
('joining_date', models.DateField(blank=True, help_text='Desired start date', null=True)),
|
||||||
('open_positions', models.PositiveIntegerField(default=1, help_text='Number of open positions for this job')),
|
('open_positions', models.PositiveIntegerField(default=1, help_text='Number of open positions for this job')),
|
||||||
|
('max_applications', models.PositiveIntegerField(blank=True, default=1000, help_text='Maximum number of applications allowed', null=True)),
|
||||||
('cancel_reason', models.TextField(blank=True, help_text='Reason for canceling the job posting', verbose_name='Cancel Reason')),
|
('cancel_reason', models.TextField(blank=True, help_text='Reason for canceling the job posting', verbose_name='Cancel Reason')),
|
||||||
('cancelled_by', models.CharField(blank=True, help_text='Name of person who cancelled this job', max_length=100, verbose_name='Cancelled By')),
|
('cancelled_by', models.CharField(blank=True, help_text='Name of person who cancelled this job', max_length=100, verbose_name='Cancelled By')),
|
||||||
('cancelled_at', models.DateTimeField(blank=True, null=True)),
|
('cancelled_at', models.DateTimeField(blank=True, null=True)),
|
||||||
@ -293,24 +270,22 @@ class Migration(migrations.Migration):
|
|||||||
name='InterviewSchedule',
|
name='InterviewSchedule',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('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')),
|
('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')),
|
('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')),
|
||||||
('start_date', models.DateField(verbose_name='Start Date')),
|
('start_date', models.DateField(db_index=True, verbose_name='Start Date')),
|
||||||
('end_date', models.DateField(verbose_name='End Date')),
|
('end_date', models.DateField(db_index=True, verbose_name='End Date')),
|
||||||
('working_days', models.JSONField(verbose_name='Working Days')),
|
('working_days', models.JSONField(verbose_name='Working Days')),
|
||||||
('start_time', models.TimeField(verbose_name='Start Time')),
|
('start_time', models.TimeField(verbose_name='Start Time')),
|
||||||
('end_time', models.TimeField(verbose_name='End Time')),
|
('end_time', models.TimeField(verbose_name='End Time')),
|
||||||
('breaks', models.JSONField(blank=True, default=list, verbose_name='Break Times')),
|
('break_start_time', models.TimeField(blank=True, null=True, verbose_name='Break Start Time')),
|
||||||
|
('break_end_time', models.TimeField(blank=True, null=True, verbose_name='Break End Time')),
|
||||||
('interview_duration', models.PositiveIntegerField(verbose_name='Interview Duration (minutes)')),
|
('interview_duration', models.PositiveIntegerField(verbose_name='Interview Duration (minutes)')),
|
||||||
('buffer_time', models.PositiveIntegerField(default=0, verbose_name='Buffer Time (minutes)')),
|
('buffer_time', models.PositiveIntegerField(default=0, verbose_name='Buffer Time (minutes)')),
|
||||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
('candidates', models.ManyToManyField(blank=True, null=True, related_name='interview_schedules', to='recruitment.candidate')),
|
||||||
('candidates', models.ManyToManyField(related_name='interview_schedules', to='recruitment.candidate')),
|
|
||||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interview_schedules', to='recruitment.jobposting')),
|
('job', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='interview_schedules', to='recruitment.jobposting')),
|
||||||
],
|
],
|
||||||
options={
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
migrations.AddField(
|
migrations.AddField(
|
||||||
model_name='formtemplate',
|
model_name='formtemplate',
|
||||||
@ -402,9 +377,9 @@ class Migration(migrations.Migration):
|
|||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')),
|
('slug', django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug')),
|
||||||
('interview_date', models.DateField(verbose_name='Interview Date')),
|
('interview_date', models.DateField(db_index=True, verbose_name='Interview Date')),
|
||||||
('interview_time', models.TimeField(verbose_name='Interview Time')),
|
('interview_time', models.TimeField(verbose_name='Interview Time')),
|
||||||
('status', models.CharField(choices=[('scheduled', 'Scheduled'), ('confirmed', 'Confirmed'), ('cancelled', 'Cancelled'), ('completed', 'Completed')], default='scheduled', max_length=20)),
|
('status', models.CharField(choices=[('scheduled', 'Scheduled'), ('confirmed', 'Confirmed'), ('cancelled', 'Cancelled'), ('completed', 'Completed')], db_index=True, default='scheduled', max_length=20)),
|
||||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
('updated_at', models.DateTimeField(auto_now=True)),
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
('candidate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scheduled_interviews', to='recruitment.candidate')),
|
('candidate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='scheduled_interviews', to='recruitment.candidate')),
|
||||||
@ -412,8 +387,92 @@ class Migration(migrations.Migration):
|
|||||||
('schedule', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interviews', to='recruitment.interviewschedule')),
|
('schedule', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='interviews', to='recruitment.interviewschedule')),
|
||||||
('zoom_meeting', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='interview', to='recruitment.zoommeeting')),
|
('zoom_meeting', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='interview', to='recruitment.zoommeeting')),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='MeetingComment',
|
||||||
|
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')),
|
||||||
|
('content', django_ckeditor_5.fields.CKEditor5Field(verbose_name='Content')),
|
||||||
|
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meeting_comments', to=settings.AUTH_USER_MODEL, verbose_name='Author')),
|
||||||
|
('meeting', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='recruitment.zoommeeting', verbose_name='Meeting')),
|
||||||
|
],
|
||||||
options={
|
options={
|
||||||
'abstract': False,
|
'verbose_name': 'Meeting Comment',
|
||||||
|
'verbose_name_plural': 'Meeting Comments',
|
||||||
|
'ordering': ['-created_at'],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FieldResponse',
|
||||||
|
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')),
|
||||||
|
('value', models.JSONField(blank=True, help_text='Response value (stored as JSON)', null=True)),
|
||||||
|
('uploaded_file', models.FileField(blank=True, null=True, upload_to='form_uploads/')),
|
||||||
|
('field', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='recruitment.formfield')),
|
||||||
|
('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='responses', to='recruitment.formsubmission')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Field Response',
|
||||||
|
'verbose_name_plural': 'Field Responses',
|
||||||
|
'indexes': [models.Index(fields=['submission'], name='recruitment_submiss_474130_idx'), models.Index(fields=['field'], name='recruitment_field_i_097e5b_idx')],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='formsubmission',
|
||||||
|
index=models.Index(fields=['submitted_at'], name='recruitment_submitt_7946c8_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='interviewschedule',
|
||||||
|
index=models.Index(fields=['start_date'], name='recruitment_start_d_15d55e_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='interviewschedule',
|
||||||
|
index=models.Index(fields=['end_date'], name='recruitment_end_dat_aeb00e_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='interviewschedule',
|
||||||
|
index=models.Index(fields=['created_by'], name='recruitment_created_d0bdcc_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='formtemplate',
|
||||||
|
index=models.Index(fields=['created_at'], name='recruitment_created_c21775_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='formtemplate',
|
||||||
|
index=models.Index(fields=['is_active'], name='recruitment_is_acti_ae5efb_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='candidate',
|
||||||
|
index=models.Index(fields=['stage'], name='recruitment_stage_f1c6eb_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='candidate',
|
||||||
|
index=models.Index(fields=['created_at'], name='recruitment_created_73590f_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='jobposting',
|
||||||
|
index=models.Index(fields=['status', 'created_at', 'title'], name='recruitment_status_8b77aa_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='jobposting',
|
||||||
|
index=models.Index(fields=['slug'], name='recruitment_slug_004045_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='scheduledinterview',
|
||||||
|
index=models.Index(fields=['job', 'status'], name='recruitment_job_id_f09e22_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='scheduledinterview',
|
||||||
|
index=models.Index(fields=['interview_date', 'interview_time'], name='recruitment_intervi_7f5877_idx'),
|
||||||
|
),
|
||||||
|
migrations.AddIndex(
|
||||||
|
model_name='scheduledinterview',
|
||||||
|
index=models.Index(fields=['candidate', 'job'], name='recruitment_candida_43d5b0_idx'),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-15 10:33
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0013_alter_formtemplate_created_by'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='close_at',
|
|
||||||
field=models.DateTimeField(blank=True, help_text='Date and time at which applications close', null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='max_applications',
|
|
||||||
field=models.PositiveIntegerField(default=1000, help_text='Maximum number of applications allowed'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-15 10:47
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0014_formtemplate_close_at_formtemplate_max_applications_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='close_at',
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, verbose_name='Created at'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True, verbose_name='Updated at'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-15 10:52
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0015_remove_formtemplate_close_at_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='max_applications',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='jobposting',
|
|
||||||
name='max_applications',
|
|
||||||
field=models.PositiveIntegerField(default=1000, help_text='Maximum number of applications allowed'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-15 15:54
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0016_remove_formtemplate_max_applications_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
name='breaks',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
name='break_end',
|
|
||||||
field=models.TimeField(blank=True, null=True, verbose_name='Break End Time'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
name='break_start',
|
|
||||||
field=models.TimeField(blank=True, null=True, verbose_name='Break Start Time'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-15 15:55
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0017_remove_interviewschedule_breaks_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
old_name='break_end',
|
|
||||||
new_name='break_end_time',
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
old_name='break_start',
|
|
||||||
new_name='break_start_time',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-15 16:07
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0018_rename_break_end_interviewschedule_break_end_time_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
name='candidates',
|
|
||||||
field=models.ManyToManyField(blank=True, null=True, related_name='interview_schedules', to='recruitment.candidate'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-15 16:09
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0019_alter_interviewschedule_candidates'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, verbose_name='Created at'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-16 13:52
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
import django_ckeditor_5.fields
|
|
||||||
import django_extensions.db.fields
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0020_alter_interviewschedule_created_at'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='MeetingComment',
|
|
||||||
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')),
|
|
||||||
('content', django_ckeditor_5.fields.CKEditor5Field(verbose_name='Content')),
|
|
||||||
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meeting_comments', to=settings.AUTH_USER_MODEL, verbose_name='Author')),
|
|
||||||
('meeting', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='recruitment.zoommeeting', verbose_name='Meeting')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Meeting Comment',
|
|
||||||
'verbose_name_plural': 'Meeting Comments',
|
|
||||||
'ordering': ['-created_at'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-16 19:39
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0021_meetingcomment'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='resume_parsed_category',
|
|
||||||
field=models.TextField(blank=True, verbose_name='Resume Parsed Category'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-16 19:47
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0022_candidate_resume_parsed_category'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='jobposting',
|
|
||||||
name='max_applications',
|
|
||||||
field=models.PositiveIntegerField(blank=True, default=1000, help_text='Maximum number of applications allowed', null=True),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.2.4 on 2025-10-17 20:58
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0023_alter_jobposting_max_applications'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='zoommeeting',
|
|
||||||
name='status',
|
|
||||||
field=models.CharField(blank=True, default='waiting', max_length=20, null=True, verbose_name='Status'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.2.4 on 2025-10-17 21:35
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0024_alter_zoommeeting_status'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='recommendation',
|
|
||||||
field=models.TextField(blank=True, verbose_name='Recommendation'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
# Generated by Django 5.2.4 on 2025-10-17 21:50
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0025_candidate_recommendation'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='resume_parsed_category',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='major_category_name',
|
|
||||||
field=models.TextField(blank=True, verbose_name='Major Category Name'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,159 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-18 17:51
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0026_remove_candidate_resume_parsed_category_and_more'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='email',
|
|
||||||
field=models.EmailField(db_index=True, max_length=254, verbose_name='Email'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='major_category_name',
|
|
||||||
field=models.TextField(blank=True, db_index=True, verbose_name='Major Category Name'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='match_score',
|
|
||||||
field=models.IntegerField(blank=True, db_index=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='stage',
|
|
||||||
field=models.CharField(choices=[('Applied', 'Applied'), ('Exam', 'Exam'), ('Interview', 'Interview'), ('Offer', 'Offer')], db_index=True, default='Applied', max_length=100, verbose_name='Stage'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formsubmission',
|
|
||||||
name='applicant_email',
|
|
||||||
field=models.EmailField(blank=True, db_index=True, help_text='Email of the applicant', max_length=254),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formsubmission',
|
|
||||||
name='submitted_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True, db_index=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
name='end_date',
|
|
||||||
field=models.DateField(db_index=True, verbose_name='End Date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
name='start_date',
|
|
||||||
field=models.DateField(db_index=True, verbose_name='Start Date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='jobposting',
|
|
||||||
name='application_deadline',
|
|
||||||
field=models.DateField(blank=True, db_index=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='jobposting',
|
|
||||||
name='published_at',
|
|
||||||
field=models.DateTimeField(blank=True, db_index=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='jobposting',
|
|
||||||
name='status',
|
|
||||||
field=models.CharField(choices=[('DRAFT', 'Draft'), ('ACTIVE', 'Active'), ('CLOSED', 'Closed'), ('CANCELLED', 'Cancelled'), ('ARCHIVED', 'Archived')], db_index=True, default='DRAFT', max_length=20),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='scheduledinterview',
|
|
||||||
name='interview_date',
|
|
||||||
field=models.DateField(db_index=True, verbose_name='Interview Date'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='scheduledinterview',
|
|
||||||
name='status',
|
|
||||||
field=models.CharField(choices=[('scheduled', 'Scheduled'), ('confirmed', 'Confirmed'), ('cancelled', 'Cancelled'), ('completed', 'Completed')], db_index=True, default='scheduled', max_length=20),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='zoommeeting',
|
|
||||||
name='meeting_id',
|
|
||||||
field=models.CharField(db_index=True, max_length=20, unique=True, verbose_name='Meeting ID'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='zoommeeting',
|
|
||||||
name='start_time',
|
|
||||||
field=models.DateTimeField(db_index=True, verbose_name='Start Time'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='zoommeeting',
|
|
||||||
name='status',
|
|
||||||
field=models.CharField(blank=True, db_index=True, default='waiting', max_length=20, null=True, verbose_name='Status'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='candidate',
|
|
||||||
index=models.Index(fields=['job', 'stage'], name='recruitment_job_id_766dbe_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='candidate',
|
|
||||||
index=models.Index(fields=['job', 'stage', 'match_score'], name='recruitment_job_id_bd6512_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='candidate',
|
|
||||||
index=models.Index(fields=['created_at'], name='recruitment_created_73590f_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='fieldresponse',
|
|
||||||
index=models.Index(fields=['submission'], name='recruitment_submiss_474130_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='fieldresponse',
|
|
||||||
index=models.Index(fields=['field'], name='recruitment_field_i_097e5b_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='formsubmission',
|
|
||||||
index=models.Index(fields=['submitted_at'], name='recruitment_submitt_7946c8_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='formtemplate',
|
|
||||||
index=models.Index(fields=['created_at'], name='recruitment_created_c21775_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='formtemplate',
|
|
||||||
index=models.Index(fields=['is_active'], name='recruitment_is_acti_ae5efb_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
index=models.Index(fields=['start_date'], name='recruitment_start_d_15d55e_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
index=models.Index(fields=['end_date'], name='recruitment_end_dat_aeb00e_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='interviewschedule',
|
|
||||||
index=models.Index(fields=['created_by'], name='recruitment_created_d0bdcc_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='jobposting',
|
|
||||||
index=models.Index(fields=['status', 'created_at'], name='recruitment_status_42c036_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='jobposting',
|
|
||||||
index=models.Index(fields=['slug'], name='recruitment_slug_004045_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='scheduledinterview',
|
|
||||||
index=models.Index(fields=['job', 'status'], name='recruitment_job_id_f09e22_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='scheduledinterview',
|
|
||||||
index=models.Index(fields=['interview_date', 'interview_time'], name='recruitment_intervi_7f5877_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='scheduledinterview',
|
|
||||||
index=models.Index(fields=['candidate', 'job'], name='recruitment_candida_43d5b0_idx'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 5.2.4 on 2025-10-18 21:54
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0027_alter_candidate_email_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='interview_status',
|
|
||||||
field=models.CharField(blank=True, choices=[('Passed', 'Passed'), ('Failed', 'Failed')], max_length=100, null=True, verbose_name='Interview Status'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
# Generated by Django 5.2.4 on 2025-10-19 10:26
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0028_alter_candidate_interview_status'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveIndex(
|
|
||||||
model_name='candidate',
|
|
||||||
name='recruitment_job_id_766dbe_idx',
|
|
||||||
),
|
|
||||||
migrations.RemoveIndex(
|
|
||||||
model_name='candidate',
|
|
||||||
name='recruitment_job_id_bd6512_idx',
|
|
||||||
),
|
|
||||||
migrations.RemoveIndex(
|
|
||||||
model_name='jobposting',
|
|
||||||
name='recruitment_status_42c036_idx',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='criteria_checklist',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='major_category_name',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='match_score',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='recommendation',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='strengths',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='weaknesses',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='candidate',
|
|
||||||
name='ai_analysis_data',
|
|
||||||
field=models.JSONField(default=dict, help_text='Full JSON output from the resume scoring model.', verbose_name='AI Analysis Data'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='candidate',
|
|
||||||
index=models.Index(fields=['stage'], name='recruitment_stage_f1c6eb_idx'),
|
|
||||||
),
|
|
||||||
migrations.AddIndex(
|
|
||||||
model_name='jobposting',
|
|
||||||
index=models.Index(fields=['status', 'created_at', 'title'], name='recruitment_status_8b77aa_idx'),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-19 13:39
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0029_remove_candidate_recruitment_job_id_766dbe_idx_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='candidate',
|
|
||||||
options={'ordering': ['-ai_analysis_data__match_score', '-created_at'], 'verbose_name': 'Candidate', 'verbose_name_plural': 'Candidates'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
# Generated by Django 5.2.6 on 2025-10-19 13:43
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0030_alter_candidate_options'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='candidate',
|
|
||||||
options={'verbose_name': 'Candidate', 'verbose_name_plural': 'Candidates'},
|
|
||||||
),
|
|
||||||
]
|
|
||||||
Binary file not shown.
@ -752,6 +752,15 @@ def form_wizard_view(request, template_id):
|
|||||||
"""Display the form as a step-by-step wizard"""
|
"""Display the form as a step-by-step wizard"""
|
||||||
template = get_object_or_404(FormTemplate, pk=template_id, is_active=True)
|
template = get_object_or_404(FormTemplate, pk=template_id, is_active=True)
|
||||||
job_id = template.job.internal_job_id
|
job_id = template.job.internal_job_id
|
||||||
|
job=template.job
|
||||||
|
is_limit_exceeded=job.is_application_limit_reached
|
||||||
|
if is_limit_exceeded:
|
||||||
|
messages.error(
|
||||||
|
request,
|
||||||
|
'Application limit reached: This job is no longer accepting new applications. Please explore other available positions.'
|
||||||
|
)
|
||||||
|
return redirect('job_detail_candidate',slug=job.slug)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"forms/form_wizard.html",
|
"forms/form_wizard.html",
|
||||||
@ -763,6 +772,8 @@ def form_wizard_view(request, template_id):
|
|||||||
def submit_form(request, template_id):
|
def submit_form(request, template_id):
|
||||||
"""Handle form submission"""
|
"""Handle form submission"""
|
||||||
template = get_object_or_404(FormTemplate, id=template_id)
|
template = get_object_or_404(FormTemplate, id=template_id)
|
||||||
|
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
try:
|
try:
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
@ -829,7 +840,7 @@ def submit_form(request, template_id):
|
|||||||
job=submission.template.job,
|
job=submission.template.job,
|
||||||
|
|
||||||
)
|
)
|
||||||
return redirect('application_success')
|
return redirect('application_success',slug=job.slug)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Candidate creation failed,{e}")
|
logger.error(f"Candidate creation failed,{e}")
|
||||||
@ -2086,10 +2097,10 @@ def set_staff_password(request,pk):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
# @login_required
|
||||||
def user_detail(requests,pk):
|
# def user_detail(requests,pk):
|
||||||
user=get_object_or_404(User,pk=pk)
|
# user=get_object_or_404(User,pk=pk)
|
||||||
return render(requests,'user/profile.html')
|
# return render(requests,'user/profile.html')
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@ -2173,3 +2184,11 @@ def edit_meeting_comment(request, slug, comment_id):
|
|||||||
return redirect('meeting_details', slug=slug)
|
return redirect('meeting_details', slug=slug)
|
||||||
else:
|
else:
|
||||||
form = MeetingCommentForm(instance=comment)
|
form = MeetingCommentForm(instance=comment)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_meeting_comment(request):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def set_meeting_candidate(request):
|
||||||
|
pass
|
||||||
187
templates/account/password_reset_done.html
Normal file
187
templates/account/password_reset_done.html
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
{% load static i18n %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{% trans "Password Reset Sent" %} - KAAUH ATS</title>
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
||||||
|
|
||||||
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* CUSTOM TEAL THEME OVERRIDES FOR BOOTSTRAP (Copied from provided login page) */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
:root {
|
||||||
|
/* Define TEAL as the primary color for Bootstrap overrides */
|
||||||
|
--bs-primary: #00636e; /* Dark Teal */
|
||||||
|
--bs-primary-rgb: 0, 99, 110;
|
||||||
|
--bs-primary-light: #007a88; /* Lighter Teal for hover */
|
||||||
|
|
||||||
|
/* Background and Text Colors */
|
||||||
|
--bs-body-bg: #f8f9fa; /* Light gray background */
|
||||||
|
--bs-body-color: #212529; /* Dark text */
|
||||||
|
|
||||||
|
/* Utility colors */
|
||||||
|
--bs-border-color: #dee2e6; /* Bootstrap default border */
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Left Panel (Replicating the original look) */
|
||||||
|
.left-panel {
|
||||||
|
flex: 1;
|
||||||
|
/* NOTE: Static image URL is included here. Ensure 'image/kaauh_banner.png' exists in your static files. */
|
||||||
|
background: url("{% static 'image/kaauh_banner.png' %}") no-repeat center center;
|
||||||
|
background-size: cover;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
padding: 3rem;
|
||||||
|
color: white;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.left-panel::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(to top, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0) 50%);
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.left-panel-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Right Panel Styling */
|
||||||
|
.right-panel {
|
||||||
|
background-color: white;
|
||||||
|
padding: 3rem;
|
||||||
|
}
|
||||||
|
.form-fields {
|
||||||
|
max-height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Component Overrides to use Teal Theme */
|
||||||
|
.btn-primary {
|
||||||
|
background-color: var(--bs-primary);
|
||||||
|
border-color: var(--bs-primary);
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 99, 110, 0.2);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.btn-primary:hover {
|
||||||
|
background-color: var(--bs-primary-light);
|
||||||
|
border-color: var(--bs-primary-light);
|
||||||
|
box-shadow: 0 6px 10px rgba(0, 99, 110, 0.3);
|
||||||
|
}
|
||||||
|
.form-control:focus {
|
||||||
|
border-color: var(--bs-primary);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(0, 99, 110, 0.25);
|
||||||
|
}
|
||||||
|
.text-accent {
|
||||||
|
color: var(--bs-primary) !important;
|
||||||
|
}
|
||||||
|
.text-accent:hover {
|
||||||
|
color: var(--bs-primary-light) !important;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.alert-info-custom {
|
||||||
|
background-color: #f0f8ff; /* Very light blue */
|
||||||
|
border-left: 5px solid var(--bs-primary);
|
||||||
|
color: var(--bs-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ADJUSTED: Custom size adjustment for right panel on desktop */
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
/* 1. Set a NARROWER fixed width for the right panel container */
|
||||||
|
.right-panel-col {
|
||||||
|
flex: 0 0 450px; /* Width of the panel */
|
||||||
|
}
|
||||||
|
/* 2. Vertically and horizontally center the content within the narrow panel */
|
||||||
|
.right-panel-col > .d-flex {
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
/* 3. Ensure the form container doesn't exceed a smaller size and is centered */
|
||||||
|
.right-panel-content-wrapper {
|
||||||
|
max-width: 350px; /* Max width of the form elements inside the panel */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="d-flex vh-100 w-100">
|
||||||
|
|
||||||
|
<div class="left-panel d-none d-lg-flex flex-grow-1">
|
||||||
|
<div class="left-panel-content">
|
||||||
|
<h1 class="text-4xl font-weight-bold mb-4" style="font-size: 1.5rem;">
|
||||||
|
<span class="text-white">
|
||||||
|
<div class="hospital-text text-center text-md-start me-3">
|
||||||
|
<div class="ar small">{% trans "جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية" %}</div>
|
||||||
|
<div class="ar small">{% trans "ومستشفى الملك عبدالله بن عبدالعزيز التخصصي" %}</div>
|
||||||
|
<div class="en small">{% trans "Princess Nourah bint Abdulrahman University" %}</div>
|
||||||
|
<div class="en small">{% trans "King Abdullah bin Abdulaziz University Hospital" %}</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<small>Powered By TENHAL | تنحل</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-column right-panel right-panel-col flex-grow-1 align-items-center justify-content-center">
|
||||||
|
|
||||||
|
<div class="right-panel-content-wrapper">
|
||||||
|
|
||||||
|
<h2 id="form-title" class="h3 fw-bold mb-4 text-center text-accent">{% trans "Password Reset Sent" %}</h2>
|
||||||
|
|
||||||
|
<div class="form-fields">
|
||||||
|
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<i class="fas fa-envelope-open-text fa-4x mb-3 text-accent" aria-hidden="true"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="text-muted text-center mb-4">
|
||||||
|
{% blocktrans %}
|
||||||
|
We've **sent an email** to the address you provided with instructions on how to reset your password.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="alert alert-info-custom p-3 d-flex align-items-start" role="alert">
|
||||||
|
<i class="fas fa-info-circle me-3 mt-1" aria-hidden="true"></i>
|
||||||
|
<small>
|
||||||
|
{% trans "Please check your inbox (and spam folder). The link in the email is temporary and will expire soon for security reasons." %}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# Button to return to the login page #}
|
||||||
|
<div class="d-grid mt-4">
|
||||||
|
<a href="{% url 'account_login' %}" class="btn btn-primary btn-lg">
|
||||||
|
<i class="fas fa-sign-in-alt me-2"></i> {% trans "Return to Login" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js" crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
237
templates/account/password_reset_from_key.html
Normal file
237
templates/account/password_reset_from_key.html
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
{% load static i18n %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{% trans "Set New Password" %} - KAAUH ATS</title>
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
||||||
|
|
||||||
|
{% get_current_language as LANGUAGE_CODE %}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* CUSTOM TEAL THEME OVERRIDES FOR BOOTSTRAP (Copied from provided login page) */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
:root {
|
||||||
|
/* Define TEAL as the primary color for Bootstrap overrides */
|
||||||
|
--bs-primary: #00636e; /* Dark Teal */
|
||||||
|
--bs-primary-rgb: 0, 99, 110;
|
||||||
|
--bs-primary-light: #007a88; /* Lighter Teal for hover */
|
||||||
|
|
||||||
|
/* Background and Text Colors */
|
||||||
|
--bs-body-bg: #f8f9fa; /* Light gray background */
|
||||||
|
--bs-body-color: #212529; /* Dark text */
|
||||||
|
|
||||||
|
/* Utility colors */
|
||||||
|
--bs-border-color: #dee2e6; /* Bootstrap default border */
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Left Panel (Replicating the original look) */
|
||||||
|
.left-panel {
|
||||||
|
flex: 1;
|
||||||
|
/* NOTE: Static image URL is included here. Ensure 'image/kaauh_banner.png' exists in your static files. */
|
||||||
|
background: url("{% static 'image/kaauh_banner.png' %}") no-repeat center center;
|
||||||
|
background-size: cover;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
padding: 3rem;
|
||||||
|
color: white;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.left-panel::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: linear-gradient(to top, rgba(0,0,0,0.8) 0%, rgba(0,0,0,0) 50%);
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.left-panel-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Right Panel Styling */
|
||||||
|
.right-panel {
|
||||||
|
background-color: white;
|
||||||
|
padding: 3rem;
|
||||||
|
}
|
||||||
|
.form-fields {
|
||||||
|
max-height: 100%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Component Overrides to use Teal Theme */
|
||||||
|
.btn-primary {
|
||||||
|
background-color: var(--bs-primary);
|
||||||
|
border-color: var(--bs-primary);
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 99, 110, 0.2);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.btn-primary:hover {
|
||||||
|
background-color: var(--bs-primary-light);
|
||||||
|
border-color: var(--bs-primary-light);
|
||||||
|
box-shadow: 0 6px 10px rgba(0, 99, 110, 0.3);
|
||||||
|
}
|
||||||
|
.form-control:focus {
|
||||||
|
border-color: var(--bs-primary);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(0, 99, 110, 0.25);
|
||||||
|
}
|
||||||
|
.text-accent {
|
||||||
|
color: var(--bs-primary) !important;
|
||||||
|
}
|
||||||
|
.text-accent:hover {
|
||||||
|
color: var(--bs-primary-light) !important;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.alert-info-custom {
|
||||||
|
background-color: #f0f8ff; /* Very light blue */
|
||||||
|
border-left: 5px solid var(--bs-primary);
|
||||||
|
color: var(--bs-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ADJUSTED: Custom size adjustment for right panel on desktop */
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
/* 1. Set a NARROWER fixed width for the right panel container */
|
||||||
|
.right-panel-col {
|
||||||
|
flex: 0 0 450px; /* Width of the panel */
|
||||||
|
}
|
||||||
|
/* 2. Vertically and horizontally center the content within the narrow panel */
|
||||||
|
.right-panel-col > .d-flex {
|
||||||
|
height: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
/* 3. Ensure the form container doesn't exceed a smaller size and is centered */
|
||||||
|
.right-panel-content-wrapper {
|
||||||
|
max-width: 350px; /* Max width of the form elements inside the panel */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="d-flex vh-100 w-100">
|
||||||
|
|
||||||
|
<div class="left-panel d-none d-lg-flex flex-grow-1">
|
||||||
|
<div class="left-panel-content">
|
||||||
|
<h1 class="text-4xl font-weight-bold mb-4" style="font-size: 1.5rem;">
|
||||||
|
<span class="text-white">
|
||||||
|
<div class="hospital-text text-center text-md-start me-3">
|
||||||
|
<div class="ar small">{% trans "جامعة الأميرة نورة بنت عبدالرحمن الأكاديمية" %}</div>
|
||||||
|
<div class="ar small">{% trans "ومستشفى الملك عبدالله بن عبدالعزيز التخصصي" %}</div>
|
||||||
|
<div class="en small">{% trans "Princess Nourah bint Abdulrahman University" %}</div>
|
||||||
|
<div class="en small">{% trans "King Abdullah bin Abdulaziz University Hospital" %}</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<small>Powered By TENHAL | تنحل</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex flex-column right-panel right-panel-col flex-grow-1 align-items-center justify-content-center">
|
||||||
|
|
||||||
|
<div class="right-panel-content-wrapper">
|
||||||
|
|
||||||
|
<h2 id="form-title" class="h3 fw-bold mb-4 text-center text-accent">{% trans "Set New Password" %}</h2>
|
||||||
|
|
||||||
|
<div class="form-fields">
|
||||||
|
|
||||||
|
{% if form %}
|
||||||
|
<p class="text-muted small mb-4 text-center">
|
||||||
|
{% trans 'Please enter your new password below. You can then log in.' %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="post" action="{{ action_url }}">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{# Display any general form errors #}
|
||||||
|
{% 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>
|
||||||
|
|
||||||
|
{% if form.password.errors %}
|
||||||
|
<div class="alert alert-danger p-2 small">
|
||||||
|
{{ form.password.errors }}
|
||||||
|
</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>
|
||||||
|
|
||||||
|
{% if form.password2.errors %}
|
||||||
|
<div class="alert alert-danger p-2 small">
|
||||||
|
{{ form.password2.errors }}
|
||||||
|
</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, if any (like uidb64, token/key) #}
|
||||||
|
{{ form.uid }}
|
||||||
|
{{ form.token }}
|
||||||
|
|
||||||
|
{# Submit Button #}
|
||||||
|
<button type="submit" name="action" class="btn btn-primary w-100 mt-4">
|
||||||
|
{% trans "Change Password" %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
{# Message when the reset key is invalid or expired #}
|
||||||
|
<h4 class="text-danger text-center mb-3">{% trans "Password Reset Failed" %}</h4>
|
||||||
|
<p class="text-muted text-center mb-4">
|
||||||
|
{% trans "The password reset link is invalid or has expired." %}
|
||||||
|
</p>
|
||||||
|
<div class="d-grid mt-4">
|
||||||
|
<a href="{% url 'account_reset_password' %}" class="btn btn-primary">
|
||||||
|
<i class="fas fa-redo me-2"></i> {% trans "Request New Reset Link" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<a href="{% url 'account_login' %}" class="small text-accent fw-medium">
|
||||||
|
<i class="fas fa-sign-in-alt me-1"></i> {% trans "Return to Sign In" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js" crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -56,7 +56,7 @@
|
|||||||
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" class="navbar-brand-mobile">
|
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" class="navbar-brand-mobile">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a class="navbar-brand text-white d-none d-lg-block" href="{% url 'dashboard' %}" aria-label="Home">
|
<a class="navbar-brand text-white d-none d-lg-block me-4 pe-4" href="{% url 'dashboard' %}" aria-label="Home">
|
||||||
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 60px; height: 60px;">
|
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 60px; height: 60px;">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
@ -179,7 +179,7 @@
|
|||||||
class="dropdown-item py-2 px-4 text-danger d-flex align-items-center border-0 bg-transparent text-start"
|
class="dropdown-item py-2 px-4 text-danger d-flex align-items-center border-0 bg-transparent text-start"
|
||||||
aria-label="{% trans 'Sign out' %}"
|
aria-label="{% trans 'Sign out' %}"
|
||||||
>
|
>
|
||||||
<i class="fas fa-sign-out-alt me-3 fs-5"></i>
|
<i class="fas fa-sign-out-alt me-3 fs-5 text-danger"></i>
|
||||||
<span>{% trans "Sign Out" %}</span>
|
<span>{% trans "Sign Out" %}</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
@ -221,7 +221,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item me-lg-4">
|
{% comment %} <li class="nav-item me-lg-4">
|
||||||
<a class="nav-link {% if request.resolver_match.url_name == 'training_list' %}active{% endif %}" href="{% url 'training_list' %}">
|
<a class="nav-link {% if request.resolver_match.url_name == 'training_list' %}active{% endif %}" href="{% url 'training_list' %}">
|
||||||
<span class="d-flex align-items-center gap-2">
|
<span class="d-flex align-items-center gap-2">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
|
||||||
@ -230,8 +230,8 @@
|
|||||||
{% trans "Training" %}
|
{% trans "Training" %}
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li> {% endcomment %}
|
||||||
<li class="nav-item dropdown ms-lg-2">
|
{% comment %} <li class="nav-item dropdown ms-lg-2">
|
||||||
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
|
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"
|
||||||
data-bs-offset="0, 8" data-bs-auto-close="outside">
|
data-bs-offset="0, 8" data-bs-auto-close="outside">
|
||||||
{% trans "More" %}
|
{% trans "More" %}
|
||||||
@ -246,7 +246,7 @@
|
|||||||
<li><a class="dropdown-item" href="#"><i class="fas fa-users me-2"></i> {% trans "All Candidates" %}</a></li>
|
<li><a class="dropdown-item" href="#"><i class="fas fa-users me-2"></i> {% trans "All Candidates" %}</a></li>
|
||||||
<li><a class="dropdown-item" href="#"><i class="fas fa-user-plus me-2"></i> {% trans "New Candidates" %}</a></li>
|
<li><a class="dropdown-item" href="#"><i class="fas fa-user-plus me-2"></i> {% trans "New Candidates" %}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li> {% endcomment %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,6 +8,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
{# ================================================= #}
|
||||||
|
{# DJANGO MESSAGE BLOCK - Placed directly below the main navbar #}
|
||||||
|
{# ================================================= #}
|
||||||
|
{% if messages %}
|
||||||
|
<div class="container-fluid message-container">
|
||||||
|
<div class="row">
|
||||||
|
{# Using responsive columns to center the message content, similar to your form structure #}
|
||||||
|
<div class="col-lg-8 offset-lg-2 col-md-10 offset-md-1 col-12">
|
||||||
|
{% for message in messages %}
|
||||||
|
{# Use 'alert-{{ message.tags }}' to apply Bootstrap styling based on Django's tag (success, error/danger, info, warning) #}
|
||||||
|
<div class="alert alert-{{ message.tags|default:'info' }} alert-dismissible fade show" role="alert">
|
||||||
|
{{ message }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{# ================================================= #}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="row mb-5 mt-3 main-content-area">
|
<div class="row mb-5 mt-3 main-content-area">
|
||||||
|
|
||||||
@ -85,4 +107,6 @@
|
|||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock content%}
|
{% endblock content%}
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{% translate "Application Form" %}</title>
|
<title>{% translate "Application Form" %}</title>
|
||||||
|
|
||||||
{% comment %} Load the correct Bootstrap CSS file for RTL/LTR {% endcomment %}
|
{% comment %} Load the correct Bootstrap CSS file for RTL/LTR {% endcomment %}
|
||||||
{% if LANGUAGE_CODE == 'ar' %}
|
{% if LANGUAGE_CODE == 'ar' %}
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.rtl.min.css" rel="stylesheet">
|
||||||
@ -27,13 +27,13 @@
|
|||||||
--kaauh-teal-dark: #004a53;
|
--kaauh-teal-dark: #004a53;
|
||||||
--success: #198754;
|
--success: #198754;
|
||||||
--danger: #dc3545;
|
--danger: #dc3545;
|
||||||
--light-bg: #f8f9fa;
|
--light-bg: #f8f9fa;
|
||||||
--gray-text: #6c757d;
|
--gray-text: #6c757d;
|
||||||
--kaauh-border: #eaeff3; /* Added for dropdown styling */
|
--kaauh-border: #eaeff3; /* Added for dropdown styling */
|
||||||
|
|
||||||
/* CALCULATED STICKY HEIGHTS */
|
/* CALCULATED STICKY HEIGHTS */
|
||||||
--navbar-height: 56px;
|
--navbar-height: 56px;
|
||||||
--navbar-gap: 16px;
|
--navbar-gap: 16px;
|
||||||
--sticky-navbar-total-height: 128px;
|
--sticky-navbar-total-height: 128px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +42,7 @@
|
|||||||
background-color: #f0f0f5; /* Light gray background for contrast */
|
background-color: #f0f0f5; /* Light gray background for contrast */
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-main-action {
|
.btn-main-action {
|
||||||
background-color: var(--kaauh-teal);
|
background-color: var(--kaauh-teal);
|
||||||
color: white;
|
color: white;
|
||||||
@ -60,7 +60,38 @@
|
|||||||
.bg-kaauh-teal-dark {
|
.bg-kaauh-teal-dark {
|
||||||
background-color: var(--kaauh-teal-dark) !important;
|
background-color: var(--kaauh-teal-dark) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
/* NEW: MESSAGES STYLING */
|
||||||
|
/* ---------------------------------------------------------------------- */
|
||||||
|
.message-container {
|
||||||
|
/* Position right below the sticky navbar (56px) with a small top margin */
|
||||||
|
margin-top: calc(var(--navbar-height) + 10px);
|
||||||
|
}
|
||||||
|
.alert {
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
box-shadow: 0 4px 8px rgba(0,0,0,0.08);
|
||||||
|
margin-bottom: 0; /* Handled by container margin */
|
||||||
|
border-left: 5px solid; /* Feature highlight */
|
||||||
|
}
|
||||||
|
.alert-success {
|
||||||
|
color: var(--success);
|
||||||
|
background-color: #d1e7dd;
|
||||||
|
border-color: var(--success);
|
||||||
|
}
|
||||||
|
.alert-error, .alert-danger {
|
||||||
|
color: var(--danger);
|
||||||
|
background-color: #f8d7da;
|
||||||
|
border-color: var(--danger);
|
||||||
|
}
|
||||||
|
.alert-info {
|
||||||
|
color: var(--kaauh-teal-dark);
|
||||||
|
background-color: #cff4fc;
|
||||||
|
border-color: var(--kaauh-teal);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
/* LANGUAGE TOGGLE STYLES (COPIED FROM MAIN LAYOUT) */
|
/* LANGUAGE TOGGLE STYLES (COPIED FROM MAIN LAYOUT) */
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
@ -111,18 +142,18 @@
|
|||||||
#topNavbar {
|
#topNavbar {
|
||||||
z-index: 1040; /* Higher than the bottom bar */
|
z-index: 1040; /* Higher than the bottom bar */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 1. Position the dark navbar below the white navbar + gap */
|
/* 1. Position the dark navbar below the white navbar + gap */
|
||||||
#bottomNavbar {
|
#bottomNavbar {
|
||||||
/* 56px (white nav) + 16px (mb-3) = 72px */
|
/* 56px (white nav) + 16px (mb-3) = 72px */
|
||||||
top: calc(var(--navbar-height) + var(--navbar-gap));
|
top: calc(var(--navbar-height) + var(--navbar-gap));
|
||||||
z-index: 1030;
|
z-index: 1030;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 2. Pushes the main content down so it's not hidden under the navbars */
|
/* 2. Pushes the main content down so it's not hidden under the navbars */
|
||||||
.main-content-area {
|
.main-content-area {
|
||||||
/* Total Sticky Height (128px) + Extra Margin (12px) = 140px */
|
/* Total Sticky Height (128px) + Extra Margin (12px) = 140px */
|
||||||
margin-top: calc(var(--sticky-navbar-total-height) + 12px);
|
margin-top: calc(var(--sticky-navbar-total-height) + 12px);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 3. Positions the sticky sidebar correctly */
|
/* 3. Positions the sticky sidebar correctly */
|
||||||
@ -137,23 +168,29 @@
|
|||||||
html[dir="rtl"] {
|
html[dir="rtl"] {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Flip Margin Utilities (m-end and m-start) */
|
/* Flip Margin Utilities (m-end and m-start) */
|
||||||
html[dir="rtl"] .ms-auto { margin-right: auto !important; margin-left: 0 !important; }
|
html[dir="rtl"] .ms-auto { margin-right: auto !important; margin-left: 0 !important; }
|
||||||
html[dir="rtl"] .me-auto { margin-left: auto !important; margin-right: 0 !important; }
|
html[dir="rtl"] .me-auto { margin-left: auto !important; margin-right: 0 !important; }
|
||||||
html[dir="rtl"] .ms-2 { margin-right: 0.5rem !important; margin-left: 0 !important; }
|
html[dir="rtl"] .ms-2 { margin-right: 0.5rem !important; margin-left: 0 !important; }
|
||||||
html[dir="rtl"] .me-2 { margin-left: 0.5rem !important; margin-right: 0 !important; }
|
html[dir="rtl"] .me-2 { margin-left: 0.5rem !important; margin-right: 0 !important; }
|
||||||
html[dir="rtl"] .me-1 { margin-left: 0.25rem !important; margin-right: 0 !important; } /* For the globe icon */
|
html[dir="rtl"] .me-1 { margin-left: 0.25rem !important; margin-right: 0 !important; } /* For the globe icon */
|
||||||
|
|
||||||
/* Flip alignment for text-end/text-start */
|
/* Flip alignment for text-end/text-start */
|
||||||
html[dir="rtl"] .text-end { text-align: left !important; }
|
html[dir="rtl"] .text-end { text-align: left !important; }
|
||||||
html[dir="rtl"] .text-start { text-align: right !important; }
|
html[dir="rtl"] .text-start { text-align: right !important; }
|
||||||
|
|
||||||
|
/* Flip border-left for RTL alerts */
|
||||||
|
html[dir="rtl"] .alert {
|
||||||
|
border-right: 5px solid;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
/* MOBILE RESPONSIVE STYLES (Below 992px) */
|
/* MOBILE RESPONSIVE STYLES (Below 992px) */
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
@media (max-width: 991.98px) {
|
@media (max-width: 991.98px) {
|
||||||
|
|
||||||
/* Ensures dropdown items in mobile menu align correctly */
|
/* Ensures dropdown items in mobile menu align correctly */
|
||||||
html[dir="rtl"] .navbar-collapse .dropdown-menu {
|
html[dir="rtl"] .navbar-collapse .dropdown-menu {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
@ -165,12 +202,12 @@
|
|||||||
#bottomNavbar {
|
#bottomNavbar {
|
||||||
top: calc(var(--navbar-height) + var(--navbar-gap));
|
top: calc(var(--navbar-height) + var(--navbar-gap));
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content-area {
|
.main-content-area {
|
||||||
/* Reduced margin-top for smaller screens */
|
/* Reduced margin-top for smaller screens */
|
||||||
margin-top: calc(var(--sticky-navbar-total-height) / 2);
|
margin-top: calc(var(--sticky-navbar-total-height) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Mobile Fixed Footer Bar for Application */
|
/* Mobile Fixed Footer Bar for Application */
|
||||||
.mobile-fixed-apply-bar {
|
.mobile-fixed-apply-bar {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -199,7 +236,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav id="topNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: white; z-index: 1040;">
|
<nav id="topNavbar" class="navbar navbar-expand-lg sticky-top" style="background-color: white; z-index: 1040;">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand text-white fw-bold" href="{% url 'kaauh_career' %}">
|
<a class="navbar-brand text-white fw-bold" href="{% url 'kaauh_career' %}">
|
||||||
@ -211,7 +248,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="navbarNav">
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
<ul class="navbar-nav ms-auto">
|
<ul class="navbar-nav ms-auto">
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link text-secondary" href="/applications/">{% translate "Applications" %}</a>
|
<a class="nav-link text-secondary" href="/applications/">{% translate "Applications" %}</a>
|
||||||
</li>
|
</li>
|
||||||
@ -223,15 +260,15 @@
|
|||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="nav-item dropdown">
|
<li class="nav-item dropdown">
|
||||||
<button class="language-toggle-btn dropdown-toggle" type="button"
|
<button class="language-toggle-btn dropdown-toggle" type="button"
|
||||||
data-bs-toggle="dropdown" data-bs-offset="0, 8" aria-expanded="false"
|
data-bs-toggle="dropdown" data-bs-offset="0, 8" aria-expanded="false"
|
||||||
aria-label="{% trans 'Toggle language menu' %}">
|
aria-label="{% trans 'Toggle language menu' %}">
|
||||||
<i class="fas fa-globe"></i>
|
<i class="fas fa-globe"></i>
|
||||||
<span class="d-inline">{{ LANGUAGE_CODE|upper }}</span>
|
<span class="d-inline">{{ LANGUAGE_CODE|upper }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<ul class="dropdown-menu {% if LANGUAGE_CODE == 'ar' %}dropdown-menu-start{% else %}dropdown-menu-end{% endif %}" aria-labelledby="navbarLanguageDropdown">
|
<ul class="dropdown-menu {% if LANGUAGE_CODE == 'ar' %}dropdown-menu-start{% else %}dropdown-menu-end{% endif %}" aria-labelledby="navbarLanguageDropdown">
|
||||||
|
|
||||||
{% comment %} English Button {% endcomment %}
|
{% comment %} English Button {% endcomment %}
|
||||||
<li>
|
<li>
|
||||||
<form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
|
<form action="{% url 'set_language' %}" method="post" class="d-inline">{% csrf_token %}
|
||||||
@ -257,6 +294,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
@ -225,7 +225,7 @@
|
|||||||
{% if active_tab == 'crud' %}
|
{% if active_tab == 'crud' %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
<td>{{ log.datetime|date:"Y-m-d H:i:s" }}</td>
|
||||||
<td>{{ log.user.get_full_name|default:log.user.username|default:"N/A" }}</td>
|
<td>{{ log.user.email|default:"N/A" }}</td>
|
||||||
<td>
|
<td>
|
||||||
<span class="badge rounded-pill
|
<span class="badge rounded-pill
|
||||||
{% if log.event_type == 1 %}badge-crud-create
|
{% if log.event_type == 1 %}badge-crud-create
|
||||||
|
|||||||
@ -4,8 +4,6 @@
|
|||||||
{% block title %}Create New Job Post - {{ block.super }}{% endblock %}
|
{% block title %}Create New Job Post - {{ block.super }}{% endblock %}
|
||||||
|
|
||||||
{% block customCSS %}
|
{% block customCSS %}
|
||||||
{# 💡 1. Add Summernote CSS Media in the head #}
|
|
||||||
{{ form.media.css }}
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* ================================================= */
|
/* ================================================= */
|
||||||
@ -77,25 +75,28 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================= */
|
/* ================================================= */
|
||||||
/* ✅ CORRECTED SUMMERNOTE FULL-WIDTH STYLING */
|
/* REMOVED SUMMERNOTE CSS AND ADDED CKEDITOR 5 BASE STYLES */
|
||||||
/* ================================================= */
|
/* ================================================= */
|
||||||
|
|
||||||
/* Make every Summernote editor fill its container */
|
/* CKEditor 5 base styling for the container */
|
||||||
.note-editor {
|
.ck-editor__editable_inline {
|
||||||
width: 100% !important;
|
/* This sets the content area height/min-height */
|
||||||
box-sizing: border-box !important;
|
min-height: 200px; /* A default height */
|
||||||
margin: 0 !important;
|
border-radius: 0 0 0.5rem 0.5rem;
|
||||||
max-width: none !important;
|
border: 1px solid #ced4da;
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set minimum heights for specific fields using sibling selector */
|
/* Override min-height for specific fields using ID */
|
||||||
#id_description + .note-editor { min-height: 300px; }
|
#id_description .ck-editor__editable_inline { min-height: 300px; }
|
||||||
#id_qualifications + .note-editor { min-height: 200px; }
|
#id_qualifications .ck-editor__editable_inline { min-height: 200px; }
|
||||||
#id_benefits + .note-editor,
|
#id_benefits .ck-editor__editable_inline,
|
||||||
#id_application_instructions + .note-editor { min-height: 150px; }
|
#id_application_instructions .ck-editor__editable_inline { min-height: 150px; }
|
||||||
|
|
||||||
|
.ck-toolbar {
|
||||||
|
border-radius: 0.5rem 0.5rem 0 0 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
{{form.media.css}}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@ -150,7 +151,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# ================================================= #}
|
{# ================================================= #}
|
||||||
{# SECTION 2: JOB CONTENT (All Summernote Fields) #}
|
{# SECTION 2: JOB CONTENT (CKEDITOR 5 Fields) #}
|
||||||
{# ================================================= #}
|
{# ================================================= #}
|
||||||
<div class="card mb-4 shadow-sm">
|
<div class="card mb-4 shadow-sm">
|
||||||
<div class="card-header-themed">
|
<div class="card-header-themed">
|
||||||
@ -193,14 +194,7 @@
|
|||||||
{% if form.salary_range.errors %}<div class="text-danger small mt-1">{{ form.salary_range.errors }}</div>{% endif %}
|
{% if form.salary_range.errors %}<div class="text-danger small mt-1">{{ form.salary_range.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% comment %} <div class="col-md-6">
|
{% comment %} (application_url comment removed for brevity) {% endcomment %}
|
||||||
<div>
|
|
||||||
<label for="{{ form.application_url.id_for_label }}" class="form-label">{% trans "Application URL" %} <span class="text-danger">*</span></label>
|
|
||||||
{{ form.application_url }}
|
|
||||||
{% if form.application_url.errors %}<div class="text-danger small mt-1">{{ form.application_url.errors }}</div>{% endif %}
|
|
||||||
<div class="form-text">{% trans "Full URL where candidates will apply" %}</div>
|
|
||||||
</div>
|
|
||||||
</div> {% endcomment %}
|
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div>
|
<div>
|
||||||
@ -261,16 +255,16 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ form.start_date.id_for_label }}" class="form-label">{% trans "Desired Start Date" %}</label>
|
<label for="{{ form.application_start_date.id_for_label }}" class="form-label">{% trans "Application Start Date" %}</label>
|
||||||
{{ form.start_date }}
|
{{ form.application_start_date }}
|
||||||
{% if form.start_date.errors %}<div class="text-danger small mt-1">{{ form.start_date.errors }}</div>{% endif %}
|
{% if form.application_start_date.errors %}<div class="text-danger small mt-1">{{ form.application_start_date.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ form.status.id_for_label }}" class="form-label">{% trans "Status" %}</label>
|
<label for="{{ form.joining_date.id_for_label }}" class="form-label">{% trans "Desired Joining Date" %}</label>
|
||||||
{{ form.status }}
|
{{ form.joining_date }}
|
||||||
{% if form.status.errors %}<div class="text-danger small mt-1">{{ form.status.errors }}</div>{% endif %}
|
{% if form.joining_date.errors %}<div class="text-danger small mt-1">{{ form.joining_date.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -300,14 +294,21 @@
|
|||||||
{% if form.reporting_to.errors %}<div class="text-danger small mt-1">{{ form.reporting_to.errors }}</div>{% endif %}
|
{% if form.reporting_to.errors %}<div class="text-danger small mt-1">{{ form.reporting_to.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ form.open_positions.id_for_label }}" class="form-label">{% trans "Open Positions" %}</label>
|
<label for="{{ form.open_positions.id_for_label }}" class="form-label">{% trans "Open Positions" %}</label>
|
||||||
{{ form.open_positions }}
|
{{ form.open_positions }}
|
||||||
{% if form.open_positions.errors %}<div class="text-danger small mt-1">{{ form.open_positions.errors }}</div>{% endif %}
|
{% if form.open_positions.errors %}<div class="text-danger small mt-1">{{ form.open_positions.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
|
<div>
|
||||||
|
<label for="{{ form.max_applications.id_for_label }}" class="form-label">{% trans "Max Applications" %}</label>
|
||||||
|
{{ form.max_applications }}
|
||||||
|
{% if form.max_applications.errors %}<div class="text-danger small mt-1">{{ form.max_applications.errors }}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ form.created_by.id_for_label }}" class="form-label">{% trans "Created By" %}</label>
|
<label for="{{ form.created_by.id_for_label }}" class="form-label">{% trans "Created By" %}</label>
|
||||||
{{ form.created_by }}
|
{{ form.created_by }}
|
||||||
@ -340,7 +341,11 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# 💡 2. Add Summernote JS Media at the end of the body #}
|
|
||||||
{{ form.media.js }}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block customJS %}
|
||||||
|
|
||||||
|
{{ form.media.js }}
|
||||||
|
{% endblock%}
|
||||||
@ -1,11 +1,9 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load static i18n %}
|
{% load static i18n %}
|
||||||
|
|
||||||
{% block title %}Edit {{ job.title }} - University ATS{% endblock %}
|
{% block title %}Create New Job Post - {{ block.super }}{% endblock %}
|
||||||
|
|
||||||
{% block customCSS %}
|
{% block customCSS %}
|
||||||
{# 💡 1. Add Summernote CSS Media in the head #}
|
|
||||||
{{ form.media.css }}
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* ================================================= */
|
/* ================================================= */
|
||||||
@ -44,7 +42,7 @@
|
|||||||
}
|
}
|
||||||
.btn-secondary:hover {
|
.btn-secondary:hover {
|
||||||
background-color: #e9ecef;
|
background-color: #e9ecef;
|
||||||
color: var(--kaauh-teal-dark); /* Reverting color to dark theme text */
|
color: white;
|
||||||
border-color: var(--kaauh-teal-dark);
|
border-color: var(--kaauh-teal-dark);
|
||||||
}
|
}
|
||||||
.card {
|
.card {
|
||||||
@ -73,41 +71,38 @@
|
|||||||
border: 1px solid #ced4da;
|
border: 1px solid #ced4da;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.375rem 0.75rem;
|
padding: 0.375rem 0.75rem;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================= */
|
/* ================================================= */
|
||||||
/* ✅ CORRECTED SUMMERNOTE FULL-WIDTH STYLING (Active Fix) */
|
/* REMOVED SUMMERNOTE CSS AND ADDED CKEDITOR 5 BASE STYLES */
|
||||||
/* ================================================= */
|
/* ================================================= */
|
||||||
|
|
||||||
/* The most aggressive, universal fix for Summernote within Bootstrap columns */
|
/* CKEditor 5 base styling for the container */
|
||||||
.col-12 .note-editor {
|
.ck-editor__editable_inline {
|
||||||
/* This compensates for the 0.75rem padding on each side of the col-12 */
|
/* This sets the content area height/min-height */
|
||||||
width: calc(100% + 1.5rem) !important;
|
min-height: 200px; /* A default height */
|
||||||
margin-left: -0.75rem !important;
|
border-radius: 0 0 0.5rem 0.5rem;
|
||||||
margin-right: -0.75rem !important;
|
border: 1px solid #ced4da;
|
||||||
|
|
||||||
/* General cleanup to maintain look */
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin-bottom: 0 !important;
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set minimum heights for specific fields using sibling selector */
|
/* Override min-height for specific fields using ID */
|
||||||
#id_description + .note-editor { min-height: 300px; }
|
#id_description .ck-editor__editable_inline { min-height: 300px; }
|
||||||
#id_qualifications + .note-editor { min-height: 200px; }
|
#id_qualifications .ck-editor__editable_inline { min-height: 200px; }
|
||||||
#id_benefits + .note-editor,
|
#id_benefits .ck-editor__editable_inline,
|
||||||
#id_application_instructions + .note-editor { min-height: 150px; }
|
#id_application_instructions .ck-editor__editable_inline { min-height: 150px; }
|
||||||
|
|
||||||
|
.ck-toolbar {
|
||||||
|
border-radius: 0.5rem 0.5rem 0 0 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
{{form.media.css}}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid py-4">
|
<div class="container-fluid py-4">
|
||||||
<h1 class="h3 mb-4 text-primary fw-bold">
|
<h1 class="h3 mb-4 text-primary fw-bold">
|
||||||
{# UPDATED TITLE FOR EDIT CONTEXT #}
|
<i class="fas fa-bullhorn me-2"></i> {% if form.instance.pk %} {% trans "Edit Job Posting" %} {% else %} {% trans "Create New Job Posting" %} {% endif %}
|
||||||
<i class="fas fa-edit me-2"></i> {% trans "Edit Job Posting" %}
|
|
||||||
{% if job.title %} - {{ job.title }} {% endif %}
|
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<form method="post" id="jobForm" class="mb-5" enctype="multipart/form-data">
|
<form method="post" id="jobForm" class="mb-5" enctype="multipart/form-data">
|
||||||
@ -151,26 +146,12 @@
|
|||||||
{% if form.workplace_type.errors %}<div class="text-danger small mt-1">{{ form.workplace_type.errors }}</div>{% endif %}
|
{% if form.workplace_type.errors %}<div class="text-danger small mt-1">{{ form.workplace_type.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
|
||||||
<div>
|
|
||||||
<label for="{{ form.application_deadline.id_for_label }}" class="form-label">{% trans "Application Deadline" %}</label>
|
|
||||||
{{ form.application_deadline }}
|
|
||||||
{% if form.application_deadline.errors %}<div class="text-danger small mt-1">{{ form.application_deadline.errors }}</div>{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<div>
|
|
||||||
<label for="{{ form.max_applications.id_for_label }}" class="form-label">{% trans "Max Number Of Applicants" %}</label>
|
|
||||||
{{ form.max_applications }}
|
|
||||||
{% if form.max_applications.errors %}<div class="text-danger small mt-1">{{ form.max_applications.errors }}</div>{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# ================================================= #}
|
{# ================================================= #}
|
||||||
{# SECTION 2: JOB CONTENT (All Summernote Fields) #}
|
{# SECTION 2: JOB CONTENT (CKEDITOR 5 Fields) #}
|
||||||
{# ================================================= #}
|
{# ================================================= #}
|
||||||
<div class="card mb-4 shadow-sm">
|
<div class="card mb-4 shadow-sm">
|
||||||
<div class="card-header-themed">
|
<div class="card-header-themed">
|
||||||
@ -181,7 +162,7 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ form.description.id_for_label }}" class="form-label">{% trans "Job Description" %} <span class="text-danger">*</span></label>
|
<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 %}
|
{% if form.description.errors %}<div class="text-danger small mt-1">{{ form.description.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -189,7 +170,7 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ form.qualifications.id_for_label }}" class="form-label">{% trans "Qualifications and Requirements" %}</label>
|
<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 %}
|
{% if form.qualifications.errors %}<div class="text-danger small mt-1">{{ form.qualifications.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -213,14 +194,7 @@
|
|||||||
{% if form.salary_range.errors %}<div class="text-danger small mt-1">{{ form.salary_range.errors }}</div>{% endif %}
|
{% if form.salary_range.errors %}<div class="text-danger small mt-1">{{ form.salary_range.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% comment %} <div class="col-md-6">
|
{% comment %} (application_url comment removed for brevity) {% endcomment %}
|
||||||
<div>
|
|
||||||
<label for="{{ form.application_url.id_for_label }}" class="form-label">{% trans "Application URL" %} <span class="text-danger">*</span></label>
|
|
||||||
{{ form.application_url }}
|
|
||||||
{% if form.application_url.errors %}<div class="text-danger small mt-1">{{ form.application_url.errors }}</div>{% endif %}
|
|
||||||
<div class="form-text">{% trans "Full URL where candidates will apply" %}</div>
|
|
||||||
</div>
|
|
||||||
</div> {% endcomment %}
|
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div>
|
<div>
|
||||||
@ -271,11 +245,26 @@
|
|||||||
{% if form.location_country.errors %}<div class="text-danger small mt-1">{{ form.location_country.errors }}</div>{% endif %}
|
{% if form.location_country.errors %}<div class="text-danger small mt-1">{{ form.location_country.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ form.start_date.id_for_label }}" class="form-label">{% trans "Desired Start Date" %}</label>
|
<label for="{{ form.application_deadline.id_for_label }}" class="form-label">{% trans "Application Deadline" %}</label>
|
||||||
{{ form.start_date }}
|
{{ form.application_deadline }}
|
||||||
{% if form.start_date.errors %}<div class="text-danger small mt-1">{{ form.start_date.errors }}</div>{% endif %}
|
{% if form.application_deadline.errors %}<div class="text-danger small mt-1">{{ form.application_deadline.errors }}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div>
|
||||||
|
<label for="{{ form.application_start_date.id_for_label }}" class="form-label">{% trans "Application Start Date" %}</label>
|
||||||
|
{{ form.application_start_date }}
|
||||||
|
{% if form.application_start_date.errors %}<div class="text-danger small mt-1">{{ form.application_start_date.errors }}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<div>
|
||||||
|
<label for="{{ form.joining_date.id_for_label }}" class="form-label">{% trans "Desired Joining Date" %}</label>
|
||||||
|
{{ form.joining_date }}
|
||||||
|
{% if form.joining_date.errors %}<div class="text-danger small mt-1">{{ form.joining_date.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -305,14 +294,21 @@
|
|||||||
{% if form.reporting_to.errors %}<div class="text-danger small mt-1">{{ form.reporting_to.errors }}</div>{% endif %}
|
{% if form.reporting_to.errors %}<div class="text-danger small mt-1">{{ form.reporting_to.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ form.open_positions.id_for_label }}" class="form-label">{% trans "Open Positions" %}</label>
|
<label for="{{ form.open_positions.id_for_label }}" class="form-label">{% trans "Open Positions" %}</label>
|
||||||
{{ form.open_positions }}
|
{{ form.open_positions }}
|
||||||
{% if form.open_positions.errors %}<div class="text-danger small mt-1">{{ form.open_positions.errors }}</div>{% endif %}
|
{% if form.open_positions.errors %}<div class="text-danger small mt-1">{{ form.open_positions.errors }}</div>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
|
<div>
|
||||||
|
<label for="{{ form.max_applications.id_for_label }}" class="form-label">{% trans "Max Applications" %}</label>
|
||||||
|
{{ form.max_applications }}
|
||||||
|
{% if form.max_applications.errors %}<div class="text-danger small mt-1">{{ form.max_applications.errors }}</div>{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="{{ form.created_by.id_for_label }}" class="form-label">{% trans "Created By" %}</label>
|
<label for="{{ form.created_by.id_for_label }}" class="form-label">{% trans "Created By" %}</label>
|
||||||
{{ form.created_by }}
|
{{ form.created_by }}
|
||||||
@ -335,19 +331,21 @@
|
|||||||
{# ACTION BUTTONS #}
|
{# ACTION BUTTONS #}
|
||||||
{# ================================================= #}
|
{# ================================================= #}
|
||||||
<div class="d-flex justify-content-between pt-2">
|
<div class="d-flex justify-content-between pt-2">
|
||||||
{# UPDATED CANCEL URL for Job Detail #}
|
<a href="{% url 'job_list' %}" class="btn btn-secondary">
|
||||||
<a href="{% url 'job_detail' job.slug %}" class="btn btn-secondary">
|
|
||||||
<i class="fas fa-arrow-left me-1"></i> {% trans "Cancel" %}
|
<i class="fas fa-arrow-left me-1"></i> {% trans "Cancel" %}
|
||||||
</a>
|
</a>
|
||||||
<button type="submit" class="btn btn-main-action">
|
<button type="submit" class="btn btn-main-action">
|
||||||
{# UPDATED BUTTON TEXT for Edit action #}
|
<i class="fas fa-save me-1"></i> {% trans "Save Job" %}
|
||||||
<i class="fas fa-save me-1"></i> {% trans "Update Job" %}
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# 💡 2. Add Summernote JS Media at the end of the body #}
|
|
||||||
{{ form.media.js }}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block customJS %}
|
||||||
|
|
||||||
|
{{ form.media.js }}
|
||||||
|
{% endblock%}
|
||||||
@ -81,7 +81,7 @@
|
|||||||
|
|
||||||
/* --- TABLE ALIGNMENT AND SIZING FIXES --- */
|
/* --- TABLE ALIGNMENT AND SIZING FIXES --- */
|
||||||
.table {
|
.table {
|
||||||
table-layout: fixed;
|
table-layout: fixed; /* Ensures width calculations are respected */
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
@ -97,14 +97,28 @@
|
|||||||
background-color: #f3f7f9;
|
background-color: #f3f7f9;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Optimized Main Table Column Widths (Total must be 100%) */
|
/*
|
||||||
.table th:nth-child(1) { width: 22%; }
|
* OPTIMIZED MAIN TABLE COLUMN WIDTHS (Total must be 100%)
|
||||||
.table th:nth-child(2) { width: 12%; }
|
* --------------------------------------------------------
|
||||||
.table th:nth-child(3) { width: 8%; }
|
* 1. Job Title/ID: 25% (Needs the most space)
|
||||||
.table th:nth-child(4) { width: 8%; }
|
* 2. Source: 10%
|
||||||
.table th:nth-child(5) { width: 50%; }
|
* 3. Max Apps: 7%
|
||||||
|
* 4. Deadline: 10%
|
||||||
|
* 5. Actions: 8%
|
||||||
|
* 6. Manage Forms: 10%
|
||||||
|
* 7. Applicants Metrics: 30% (Colspan 5)
|
||||||
|
* TOTAL: 25 + 10 + 7 + 10 + 8 + 10 + 30 = 100%
|
||||||
|
*/
|
||||||
|
.table th:nth-child(1) { width: 20%; } /* Job Title */
|
||||||
|
.table th:nth-child(2) { width: 10%; } /* Source */
|
||||||
|
.table th:nth-child(3) { width: 7%; } /* Max Apps */
|
||||||
|
.table th:nth-child(4) { width: 10%; } /* Deadline */
|
||||||
|
.table th:nth-child(5) { width: 8%; } /* Actions */
|
||||||
|
.table th:nth-child(6) { width: 10%; } /* Manage Forms */
|
||||||
|
/* The 7th column (Metrics) is 30% and is handled by its colspan */
|
||||||
|
|
||||||
/* Candidate Management Header Row (The one with P/F) */
|
|
||||||
|
/* Candidate Management Header Row (The one with the stage names) */
|
||||||
.nested-metrics-row th {
|
.nested-metrics-row th {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #6c757d;
|
color: #6c757d;
|
||||||
@ -114,23 +128,13 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
border-left: 1px solid var(--kaauh-border);
|
border-left: 1px solid var(--kaauh-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Metrics Sub-Column Widths (7 total sub-columns, total 30%) */
|
||||||
|
/* We have 5 main metrics: Applied, Screened, Exam, Interview, Offer.
|
||||||
|
* Let's allocate the 30% evenly: 30% / 5 = 6% per metric column.
|
||||||
|
*/
|
||||||
.nested-metrics-row th {
|
.nested-metrics-row th {
|
||||||
width: calc(50% / 7);
|
width: 6%; /* 30% / 5 metrics = 6% per metric column */
|
||||||
}
|
|
||||||
.nested-metrics-row th[colspan="2"] {
|
|
||||||
width: calc(50% / 7 * 2);
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Inner P/F Headers */
|
|
||||||
.nested-stage-metrics {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-around;
|
|
||||||
padding-top: 5px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--kaauh-teal-dark);
|
|
||||||
font-size: 0.7rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Main TH for Candidate Management Header Title */
|
/* Main TH for Candidate Management Header Title */
|
||||||
@ -143,7 +147,7 @@
|
|||||||
color: var(--kaauh-teal-dark);
|
color: var(--kaauh-teal-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Candidate Management Data Cells (7 columns total) */
|
/* Candidate Management Data Cells (5 columns total for metrics) */
|
||||||
.candidate-data-cell {
|
.candidate-data-cell {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
@ -154,7 +158,8 @@
|
|||||||
.table tbody td.candidate-data-cell:not(:first-child) {
|
.table tbody td.candidate-data-cell:not(:first-child) {
|
||||||
border-left: 1px solid var(--kaauh-border);
|
border-left: 1px solid var(--kaauh-border);
|
||||||
}
|
}
|
||||||
.table tbody tr td:nth-child(5) {
|
/* Adds a distinctive vertical line before the metrics group (7th column) */
|
||||||
|
.table tbody tr td:nth-child(7) {
|
||||||
border-left: 2px solid var(--kaauh-teal);
|
border-left: 2px solid var(--kaauh-teal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +175,7 @@
|
|||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Additional CSS for Card View layout */
|
/* Additional CSS for Card View layout (rest of your styles...) */
|
||||||
.card-view .card {
|
.card-view .card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
@ -193,6 +198,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid py-4">
|
<div class="container-fluid py-4">
|
||||||
|
{# ... (Rest of the header and filter content) ... #}
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
<h1 style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
<h1 style="color: var(--kaauh-teal-dark); font-weight: 700;">
|
||||||
<i class="fas fa-briefcase me-2"></i> {% trans "Job Postings" %}
|
<i class="fas fa-briefcase me-2"></i> {% trans "Job Postings" %}
|
||||||
@ -202,6 +208,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{# ... (Filter card) ... #}
|
||||||
<div class="card mb-4 shadow-sm no-hover">
|
<div class="card mb-4 shadow-sm no-hover">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -248,7 +255,6 @@
|
|||||||
|
|
||||||
{# --- START OF JOB LIST CONTAINER --- #}
|
{# --- START OF JOB LIST CONTAINER --- #}
|
||||||
<div id="job-list">
|
<div id="job-list">
|
||||||
{# View Switcher (Contains the Card/Table buttons and JS/CSS logic) #}
|
|
||||||
{% include "includes/_list_view_switcher.html" with list_id="job-list" %}
|
{% include "includes/_list_view_switcher.html" with list_id="job-list" %}
|
||||||
|
|
||||||
{# 1. TABLE VIEW (Default Active) #}
|
{# 1. TABLE VIEW (Default Active) #}
|
||||||
@ -260,12 +266,12 @@
|
|||||||
{# --- Corrected Multi-Row Header Structure --- #}
|
{# --- Corrected Multi-Row Header Structure --- #}
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" rowspan="2" style="width: 22%;">{% trans "Job Title / ID" %}</th>
|
<th scope="col" rowspan="2">{% trans "Job Title / ID" %}</th>
|
||||||
<th scope="col" rowspan="2" style="width: 12%;">{% trans "Source" %}</th>
|
<th scope="col" rowspan="2">{% trans "Source" %}</th>
|
||||||
<th scope="col" rowspan="2" style="width: 10%;">{% trans "Number Of Applicants" %}</th>
|
<th scope="col" rowspan="2">{% trans "Max Apps" %}</th>
|
||||||
<th scope="col" rowspan="2" style="width: 8%;">{% trans "Application Deadline" %}</th>
|
<th scope="col" rowspan="2">{% trans "Deadline" %}</th>
|
||||||
<th scope="col" rowspan="2" style="width: 8%;">{% trans "Actions" %}</th>
|
<th scope="col" rowspan="2">{% trans "Actions" %}</th>
|
||||||
<th scope="col" rowspan="2" class="text-center" style="width: 8%;">{% trans "Manage Forms" %}</th>
|
<th scope="col" rowspan="2" class="text-center">{% trans "Manage Forms" %}</th>
|
||||||
|
|
||||||
<th scope="col" colspan="5" class="candidate-management-header-title">
|
<th scope="col" colspan="5" class="candidate-management-header-title">
|
||||||
{% trans "Applicants Metrics" %}
|
{% trans "Applicants Metrics" %}
|
||||||
@ -273,11 +279,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr class="nested-metrics-row">
|
<tr class="nested-metrics-row">
|
||||||
<th style="width: calc(50% / 7);">{% trans "Applied" %}</th>
|
<th scope="col">{% trans "Applied" %}</th>
|
||||||
<th style="width: calc(50% / 7);">{% trans "Screened" %}</th>
|
<th scope="col">{% trans "Screened" %}</th>
|
||||||
<th style="width: calc(50% / 7 * 2);">{% trans "Exam" %}</th>
|
<th scope="col">{% trans "Exam" %}</th>
|
||||||
<th style="width: calc(50% / 7 * 2);">{% trans "Interview" %}</th>
|
<th scope="col">{% trans "Interview" %}</th>
|
||||||
<th style="width: calc(50% / 7);">{% trans "Offer" %}</th>
|
<th scope="col">{% trans "Offer" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
@ -333,7 +339,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# 2. CARD VIEW (Previously Missing) - Added Bootstrap row/col structure for layout #}
|
{# ... (Card View and Paginator content) ... #}
|
||||||
<div class="card-view row g-4">
|
<div class="card-view row g-4">
|
||||||
{% for job in jobs %}
|
{% for job in jobs %}
|
||||||
<div class="col-xl-4 col-lg-6 col-md-6">
|
<div class="col-xl-4 col-lg-6 col-md-6">
|
||||||
|
|||||||
@ -149,59 +149,63 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card mb-4 shadow-sm no-hover">
|
<div class="card mb-4 shadow-sm no-hover">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row g-4">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="search" class="form-label small text-muted">{% trans "Search by Name or Email" %}</label>
|
<label for="search" class="form-label small text-muted">{% trans "Search by Name or Email" %}</label>
|
||||||
<div class="input-group input-group-lg mb-3">
|
<div class="input-group input-group-lg">
|
||||||
<form method="get" action="" class="w-100">
|
<form method="get" action="" class="w-100">
|
||||||
{% include 'includes/search_form.html' %}
|
{% include 'includes/search_form.html' %}
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
{% url 'candidate_list' as candidate_list_url %}
|
|
||||||
|
|
||||||
<form method="GET" class="row g-3 align-items-end" >
|
|
||||||
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
|
|
||||||
|
|
||||||
<div class="col-md-4">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<label for="job_filter" class="form-label small text-muted">{% trans "Filter by Job" %}</label>
|
|
||||||
<div class="d-flex gap-2">
|
|
||||||
<select name="job" id="job_filter" class="form-select form-select-sm">
|
|
||||||
<option value="">{% trans "All Jobs" %}</option>
|
|
||||||
{% for job in available_jobs %}
|
|
||||||
<option value="{{ job.slug }}" {% if job_filter == job.slug %}selected{% endif %}>{{ job.title }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<select name="stage" id="stage_filter" class="form-select form-select-sm">
|
|
||||||
<option value="">{% trans "All Stages" %}</option>
|
|
||||||
<option value="Applied" {% if stage_filter == 'Applied' %}selected{% endif %}>{% trans "Applied" %}</option>
|
|
||||||
<option value="Exam" {% if stage_filter == 'Exam' %}selected{% endif %}>{% trans "Exam" %}</option>
|
|
||||||
<option value="Interview" {% if stage_filter == 'Interview' %}selected{% endif %}>{% trans "Interview" %}</option>
|
|
||||||
<option value="Offer" {% if stage_filter == 'Offer' %}selected{% endif %}>{% trans "Offer" %}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-md-8">
|
|
||||||
<div class="filter-buttons">
|
|
||||||
<button type="submit" class="btn btn-main-action btn-sm">
|
|
||||||
<i class="fas fa-filter me-1"></i> {% trans "Apply Filters" %}
|
|
||||||
</button>
|
|
||||||
{% if job_filter or search_query %}
|
|
||||||
<a href="{% url 'candidate_list' %}" class="btn btn-outline-secondary btn-sm">
|
|
||||||
<i class="fas fa-times me-1"></i> {% trans "Clear" %}
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
{% url 'candidate_list' as candidate_list_url %}
|
||||||
|
|
||||||
|
<form method="GET" class="row g-3 align-items-end h-100">
|
||||||
|
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
|
||||||
|
|
||||||
|
{# Filter Group #}
|
||||||
|
<div class="col-md-4 col-6">
|
||||||
|
<label for="job_filter" class="form-label small text-muted">{% trans "Filter by Job" %}</label>
|
||||||
|
<select name="job" id="job_filter" class="form-select form-select-sm">
|
||||||
|
<option value="">{% trans "All Jobs" %}</option>
|
||||||
|
{% for job in available_jobs %}
|
||||||
|
<option value="{{ job.slug }}" {% if job_filter == job.slug %}selected{% endif %}>{{ job.title }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4 col-6">
|
||||||
|
<label for="stage_filter" class="form-label small text-muted">{% trans "Filter by Stage" %}</label>
|
||||||
|
<select name="stage" id="stage_filter" class="form-select form-select-sm">
|
||||||
|
<option value="">{% trans "All Stages" %}</option>
|
||||||
|
<option value="Applied" {% if stage_filter == 'Applied' %}selected{% endif %}>{% trans "Applied" %}</option>
|
||||||
|
<option value="Exam" {% if stage_filter == 'Exam' %}selected{% endif %}>{% trans "Exam" %}</option>
|
||||||
|
<option value="Interview" {% if stage_filter == 'Interview' %}selected{% endif %}>{% trans "Interview" %}</option>
|
||||||
|
<option value="Offer" {% if stage_filter == 'Offer' %}selected{% endif %}>{% trans "Offer" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# Buttons Group (pushed to the right/bottom) #}
|
||||||
|
<div class="col-md-4 d-flex justify-content-end align-self-end">
|
||||||
|
<div class="filter-buttons">
|
||||||
|
<button type="submit" class="btn btn-main-action btn-sm">
|
||||||
|
<i class="fas fa-filter me-1"></i> {% trans "Apply" %}
|
||||||
|
</button>
|
||||||
|
{% if job_filter or stage_filter or search_query %}
|
||||||
|
<a href="{% url 'candidate_list' %}" class="btn btn-outline-secondary btn-sm">
|
||||||
|
<i class="fas fa-times me-1"></i> {% trans "Clear" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% if candidates %}
|
{% if candidates %}
|
||||||
<div id="candidate-list">
|
<div id="candidate-list">
|
||||||
{# View Switcher - list_id must match the container ID #}
|
{# View Switcher - list_id must match the container ID #}
|
||||||
|
|||||||
@ -108,7 +108,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="container-fluid mt-4">
|
<div class="container-fluid mt-5">
|
||||||
<div class="row px-lg-4">
|
<div class="row px-lg-4">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h1 class="h3 fw-bold dashboard-header">
|
<h1 class="h3 fw-bold dashboard-header">
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="d-flex vh-80 w-100 justify-content-center align-items-center mt-4">
|
<div class="d-flex vh-80 w-100 justify-content-center align-items-center mt-5">
|
||||||
|
|
||||||
<div class="form-card">
|
<div class="form-card">
|
||||||
|
|
||||||
@ -70,7 +70,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="pt-5 mt-2 text-center border-top border-light-subtle">
|
<div class="pt-3 mt-1 text-center border-top border-light-subtle mb-4">
|
||||||
<p class="text-muted small mb-0">
|
<p class="text-muted small mb-0">
|
||||||
<i class="fas fa-arrow-left me-1"></i>
|
<i class="fas fa-arrow-left me-1"></i>
|
||||||
<a href="{% url 'admin_settings' %}" class="text-accent text-decoration-none text-secondary">{% trans "Back to Settings" %}</a>
|
<a href="{% url 'admin_settings' %}" class="text-accent text-decoration-none text-secondary">{% trans "Back to Settings" %}</a>
|
||||||
|
|||||||
@ -122,7 +122,7 @@
|
|||||||
<div class="card p-5">
|
<div class="card p-5">
|
||||||
<h5 class="fw-bold mb-4 text-accent">{% trans "Personal Information" %}</h5>
|
<h5 class="fw-bold mb-4 text-accent">{% trans "Personal Information" %}</h5>
|
||||||
|
|
||||||
<form method="post" action="{% url 'user_detail' user.pk %}">
|
<form method="POST" action="{% url 'user_detail' user.pk %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
||||||
<div class="row g-4"> <div class="col-md-6">
|
<div class="row g-4"> <div class="col-md-6">
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="d-flex vh-80 w-100 justify-content-center align-items-center">
|
<div class="d-flex vh-80 w-100 justify-content-center align-items-center mt-5">
|
||||||
|
|
||||||
<div class="form-card">
|
<div class="form-card">
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user