few issue resolved and some ui incosistency resolved
This commit is contained in:
parent
22978a4af5
commit
5db2e09ac8
6
.env
6
.env
@ -1,3 +1,3 @@
|
|||||||
DB_NAME=norahuniversity
|
DB_NAME=haikal_db
|
||||||
DB_USER=norahuniversity
|
DB_USER=faheed
|
||||||
DB_PASSWORD=norahuniversity
|
DB_PASSWORD=Faheed@215
|
||||||
@ -318,14 +318,7 @@ class PersonForm(forms.ModelForm):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
return email.strip()
|
return email.strip()
|
||||||
def clean_gpa(self):
|
|
||||||
gpa=self.cleaned_data.get('gpa')
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ApplicationForm(forms.ModelForm):
|
class ApplicationForm(forms.ModelForm):
|
||||||
@ -786,6 +779,8 @@ class StaffUserCreationForm(UserCreationForm):
|
|||||||
user.first_name = self.cleaned_data["first_name"]
|
user.first_name = self.cleaned_data["first_name"]
|
||||||
user.last_name = self.cleaned_data["last_name"]
|
user.last_name = self.cleaned_data["last_name"]
|
||||||
user.username = self.generate_username(user.email)
|
user.username = self.generate_username(user.email)
|
||||||
|
user.password1=self.cleaned_data["password1"]
|
||||||
|
user.password2=self.cleaned_data["password2"]
|
||||||
user.is_staff = True
|
user.is_staff = True
|
||||||
if commit:
|
if commit:
|
||||||
user.save()
|
user.save()
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 5.2.7 on 2025-12-11 11:55
|
# Generated by Django 5.2.7 on 2025-12-11 14:18
|
||||||
|
|
||||||
import django.contrib.auth.models
|
import django.contrib.auth.models
|
||||||
import django.contrib.auth.validators
|
import django.contrib.auth.validators
|
||||||
@ -97,6 +97,8 @@ class Migration(migrations.Migration):
|
|||||||
('start_time', models.DateTimeField(db_index=True, verbose_name='Start Time')),
|
('start_time', models.DateTimeField(db_index=True, verbose_name='Start Time')),
|
||||||
('duration', models.PositiveIntegerField(verbose_name='Duration (minutes)')),
|
('duration', models.PositiveIntegerField(verbose_name='Duration (minutes)')),
|
||||||
('status', models.CharField(choices=[('waiting', 'Waiting'), ('started', 'Started'), ('ended', 'Ended'), ('cancelled', 'Cancelled')], db_index=True, default='waiting', max_length=20)),
|
('status', models.CharField(choices=[('waiting', 'Waiting'), ('started', 'Started'), ('ended', 'Ended'), ('cancelled', 'Cancelled')], db_index=True, default='waiting', max_length=20)),
|
||||||
|
('cancelled_at', models.DateTimeField(blank=True, null=True, verbose_name='Cancelled At')),
|
||||||
|
('cancelled_reason', models.TextField(blank=True, null=True, verbose_name='Cancellation Reason')),
|
||||||
('meeting_id', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='External Meeting ID')),
|
('meeting_id', models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='External Meeting ID')),
|
||||||
('password', models.CharField(blank=True, max_length=20, null=True)),
|
('password', models.CharField(blank=True, max_length=20, null=True)),
|
||||||
('zoom_gateway_response', models.JSONField(blank=True, null=True)),
|
('zoom_gateway_response', models.JSONField(blank=True, null=True)),
|
||||||
@ -189,7 +191,7 @@ class Migration(migrations.Migration):
|
|||||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
('first_name', secured_fields.fields.EncryptedCharField(blank=True, max_length=150, verbose_name='first name')),
|
('first_name', secured_fields.fields.EncryptedCharField(blank=True, max_length=150, verbose_name='first name')),
|
||||||
('user_type', models.CharField(choices=[('staff', 'Staff'), ('agency', 'Agency'), ('candidate', 'Candidate')], default='staff', max_length=20, verbose_name='User Type')),
|
('user_type', models.CharField(choices=[('staff', 'Staff'), ('agency', 'Agency'), ('candidate', 'Candidate')], db_index=True, default='staff', max_length=20, verbose_name='User Type')),
|
||||||
('phone', secured_fields.fields.EncryptedCharField(blank=True, null=True, verbose_name='Phone')),
|
('phone', secured_fields.fields.EncryptedCharField(blank=True, null=True, verbose_name='Phone')),
|
||||||
('profile_image', models.ImageField(blank=True, null=True, upload_to='profile_pic/', validators=[recruitment.validators.validate_image_size], verbose_name='Profile Image')),
|
('profile_image', models.ImageField(blank=True, null=True, upload_to='profile_pic/', validators=[recruitment.validators.validate_image_size], verbose_name='Profile Image')),
|
||||||
('designation', models.CharField(blank=True, max_length=100, null=True, verbose_name='Designation')),
|
('designation', models.CharField(blank=True, max_length=100, null=True, verbose_name='Designation')),
|
||||||
@ -586,7 +588,7 @@ class Migration(migrations.Migration):
|
|||||||
('phone', secured_fields.fields.EncryptedCharField(blank=True, null=True, verbose_name='Phone')),
|
('phone', secured_fields.fields.EncryptedCharField(blank=True, null=True, verbose_name='Phone')),
|
||||||
('date_of_birth', models.DateField(blank=True, null=True, verbose_name='Date of Birth')),
|
('date_of_birth', models.DateField(blank=True, null=True, verbose_name='Date of Birth')),
|
||||||
('gender', models.CharField(blank=True, choices=[('M', 'Male'), ('F', 'Female')], max_length=1, null=True, verbose_name='Gender')),
|
('gender', models.CharField(blank=True, choices=[('M', 'Male'), ('F', 'Female')], max_length=1, null=True, verbose_name='Gender')),
|
||||||
('gpa', models.DecimalField(decimal_places=2, help_text='GPA must be between 0 and 4.', max_digits=3, verbose_name='GPA')),
|
('gpa', models.DecimalField(decimal_places=2, help_text='GPA must be between 0 and 4.', max_digits=3, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(4)], verbose_name='GPA')),
|
||||||
('national_id', secured_fields.fields.EncryptedCharField(help_text='Enter the national id or iqama number')),
|
('national_id', secured_fields.fields.EncryptedCharField(help_text='Enter the national id or iqama number')),
|
||||||
('nationality', django_countries.fields.CountryField(blank=True, max_length=2, null=True, verbose_name='Nationality')),
|
('nationality', django_countries.fields.CountryField(blank=True, max_length=2, null=True, verbose_name='Nationality')),
|
||||||
('address', models.TextField(blank=True, null=True, verbose_name='Address')),
|
('address', models.TextField(blank=True, null=True, verbose_name='Address')),
|
||||||
|
|||||||
@ -1,199 +0,0 @@
|
|||||||
# Generated by Django 6.0 on 2025-12-10 21:04
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='formfield',
|
|
||||||
options={'ordering': ['order']},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='formstage',
|
|
||||||
options={'ordering': ['order']},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='formtemplate',
|
|
||||||
options={'ordering': ['-created_at']},
|
|
||||||
),
|
|
||||||
migrations.RemoveIndex(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='recruitment_created_c21775_idx',
|
|
||||||
),
|
|
||||||
migrations.RemoveIndex(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='recruitment_is_acti_ae5efb_idx',
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='formfield',
|
|
||||||
old_name='required_message',
|
|
||||||
new_name='error_message',
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='formfield',
|
|
||||||
unique_together={('stage', 'order')},
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='help_text',
|
|
||||||
field=models.CharField(blank=True, max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_date',
|
|
||||||
field=models.DateField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_date',
|
|
||||||
field=models.DateField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='field_type',
|
|
||||||
field=models.CharField(choices=[('text', 'Text Input'), ('email', 'Email'), ('phone', 'Phone'), ('textarea', 'Text Area'), ('file', 'File Upload'), ('date', 'Date Picker'), ('select', 'Dropdown'), ('radio', 'Radio Buttons'), ('checkbox', 'Checkboxes'), ('number', 'Number')], max_length=20),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='file_types',
|
|
||||||
field=models.CharField(blank=True, default='.pdf,.doc,.docx,.jpg,.jpeg,.png', max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='is_predefined',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='label',
|
|
||||||
field=models.CharField(max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_file_size',
|
|
||||||
field=models.PositiveIntegerField(default=5, help_text='Maximum file size in MB'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_files',
|
|
||||||
field=models.PositiveIntegerField(default=1),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_length',
|
|
||||||
field=models.PositiveIntegerField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_length',
|
|
||||||
field=models.PositiveIntegerField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='multiple_files',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='options',
|
|
||||||
field=models.JSONField(blank=True, default=list),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='order',
|
|
||||||
field=models.PositiveIntegerField(default=0),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='placeholder',
|
|
||||||
field=models.CharField(blank=True, max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='required',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='validation_pattern',
|
|
||||||
field=models.CharField(blank=True, choices=[('', 'None'), ('email', 'Email'), ('phone', 'Phone'), ('url', 'URL'), ('number', 'Number'), ('alpha', 'Letters Only'), ('alphanum', 'Letters & Numbers'), ('custom', 'Custom Pattern')], max_length=50),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formstage',
|
|
||||||
name='is_predefined',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formstage',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formstage',
|
|
||||||
name='order',
|
|
||||||
field=models.PositiveIntegerField(default=0),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='created_at',
|
|
||||||
field=models.DateTimeField(auto_now_add=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='created_by',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='description',
|
|
||||||
field=models.TextField(blank=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='is_active',
|
|
||||||
field=models.BooleanField(default=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='slug',
|
|
||||||
field=models.SlugField(blank=True, unique=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True),
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='formstage',
|
|
||||||
unique_together={('template', 'order')},
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='is_required',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_file_size',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_image_height',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_image_width',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,201 +0,0 @@
|
|||||||
# Generated by Django 6.0 on 2025-12-10 21:21
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
import django_extensions.db.fields
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0002_alter_formfield_options_alter_formstage_options_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='formfield',
|
|
||||||
options={'ordering': ['order'], 'verbose_name': 'Form Field', 'verbose_name_plural': 'Form Fields'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='formstage',
|
|
||||||
options={'ordering': ['order'], 'verbose_name': 'Form Stage', 'verbose_name_plural': 'Form Stages'},
|
|
||||||
),
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='formtemplate',
|
|
||||||
options={'ordering': ['-created_at'], 'verbose_name': 'Form Template', 'verbose_name_plural': 'Form Templates'},
|
|
||||||
),
|
|
||||||
migrations.RenameField(
|
|
||||||
model_name='formfield',
|
|
||||||
old_name='error_message',
|
|
||||||
new_name='required_message',
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='formfield',
|
|
||||||
unique_together=set(),
|
|
||||||
),
|
|
||||||
migrations.AlterUniqueTogether(
|
|
||||||
name='formstage',
|
|
||||||
unique_together=set(),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='is_required',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_file_size',
|
|
||||||
field=models.FloatField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_image_height',
|
|
||||||
field=models.IntegerField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_image_width',
|
|
||||||
field=models.IntegerField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='field_type',
|
|
||||||
field=models.CharField(choices=[('text', 'Text Input'), ('email', 'Email'), ('phone', 'Phone'), ('textarea', 'Text Area'), ('file', 'File Upload'), ('date', 'Date Picker'), ('select', 'Dropdown'), ('radio', 'Radio Buttons'), ('checkbox', 'Checkboxes')], help_text='Type of the field', max_length=20),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='file_types',
|
|
||||||
field=models.CharField(blank=True, help_text="Allowed file types (comma-separated, e.g., '.pdf,.doc,.docx')", max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='is_predefined',
|
|
||||||
field=models.BooleanField(default=False, help_text='Whether this is a default field'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='label',
|
|
||||||
field=models.CharField(help_text='Label for the field', max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_file_size',
|
|
||||||
field=models.PositiveIntegerField(default=5, help_text='Maximum file size in MB (default: 5MB)'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_files',
|
|
||||||
field=models.PositiveIntegerField(default=1, help_text='Maximum number of files allowed (when multiple_files is True)'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_length',
|
|
||||||
field=models.IntegerField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_length',
|
|
||||||
field=models.IntegerField(blank=True, null=True),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='multiple_files',
|
|
||||||
field=models.BooleanField(default=False, help_text='Allow multiple files to be uploaded'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='options',
|
|
||||||
field=models.JSONField(blank=True, default=list, help_text='Options for selection fields (stored as JSON array)'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='order',
|
|
||||||
field=models.PositiveIntegerField(default=0, help_text='Order of the field in the stage'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='placeholder',
|
|
||||||
field=models.CharField(blank=True, help_text='Placeholder text', max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='required',
|
|
||||||
field=models.BooleanField(default=False, help_text='Whether the field is required'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='validation_pattern',
|
|
||||||
field=models.CharField(blank=True, choices=[('', 'None'), ('email', 'Email'), ('phone', 'Phone'), ('url', 'URL'), ('number', 'Number'), ('alpha', 'Letters Only'), ('alphanum', 'Letters & Numbers'), ('custom', 'Custom')], max_length=50),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formstage',
|
|
||||||
name='is_predefined',
|
|
||||||
field=models.BooleanField(default=False, help_text='Whether this is a default resume stage'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formstage',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(help_text='Name of the stage', max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formstage',
|
|
||||||
name='order',
|
|
||||||
field=models.PositiveIntegerField(default=0, help_text='Order of the stage in the form'),
|
|
||||||
),
|
|
||||||
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='created_by',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='form_templates', to=settings.AUTH_USER_MODEL),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='description',
|
|
||||||
field=models.TextField(blank=True, help_text='Description of the form template'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='is_active',
|
|
||||||
field=models.BooleanField(default=False, help_text='Whether this template is active'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='name',
|
|
||||||
field=models.CharField(help_text='Name of the form template', max_length=200),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='slug',
|
|
||||||
field=django_extensions.db.fields.RandomCharField(blank=True, editable=False, length=8, unique=True, verbose_name='Slug'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formtemplate',
|
|
||||||
name='updated_at',
|
|
||||||
field=models.DateTimeField(auto_now=True, verbose_name='Updated at'),
|
|
||||||
),
|
|
||||||
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.RemoveField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='help_text',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_date',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_date',
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
# Generated by Django 6.0 on 2025-12-11 12:34
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0003_alter_formfield_options_alter_formstage_options_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='formfield',
|
|
||||||
options={'ordering': ['order']},
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='is_required',
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='field_type',
|
|
||||||
field=models.CharField(choices=[('text', 'Text Input'), ('number', 'Number Input'), ('email', 'Email'), ('phone', 'Phone'), ('textarea', 'Text Area'), ('file', 'File Upload'), ('date', 'Date Picker'), ('select', 'Dropdown'), ('radio', 'Radio Buttons'), ('checkbox', 'Checkboxes')], help_text='Type of the field', max_length=20),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_value',
|
|
||||||
field=models.CharField(blank=True, help_text='Max value/date for Number/Date fields', max_length=50),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_value',
|
|
||||||
field=models.CharField(blank=True, help_text='Min value/date for Number/Date fields', max_length=50),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
# Generated by Django 6.0 on 2025-12-11 13:33
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('recruitment', '0004_alter_formfield_options_remove_formfield_is_required_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterModelOptions(
|
|
||||||
name='formfield',
|
|
||||||
options={'ordering': ['order'], 'verbose_name': 'Form Field', 'verbose_name_plural': 'Form Fields'},
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='is_required',
|
|
||||||
field=models.BooleanField(default=False),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='field_type',
|
|
||||||
field=models.CharField(choices=[('text', 'Text Input'), ('email', 'Email'), ('phone', 'Phone'), ('textarea', 'Text Area'), ('file', 'File Upload'), ('date', 'Date Picker'), ('select', 'Dropdown'), ('radio', 'Radio Buttons'), ('checkbox', 'Checkboxes')], help_text='Type of the field', max_length=20),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='max_value',
|
|
||||||
field=models.CharField(blank=True, max_length=50),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='formfield',
|
|
||||||
name='min_value',
|
|
||||||
field=models.CharField(blank=True, max_length=50),
|
|
||||||
),
|
|
||||||
]
|
|
||||||
@ -187,6 +187,7 @@ class PersonListView(StaffRequiredMixin, ListView, LoginRequiredMixin):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = super().get_queryset().select_related("user")
|
queryset = super().get_queryset().select_related("user")
|
||||||
search_query = self.request.GET.get("search", "")
|
search_query = self.request.GET.get("search", "")
|
||||||
|
print(Person.objects.first().last_name)
|
||||||
if search_query:
|
if search_query:
|
||||||
queryset=queryset.filter(
|
queryset=queryset.filter(
|
||||||
Q(first_name=search_query) |
|
Q(first_name=search_query) |
|
||||||
@ -225,22 +226,26 @@ class PersonCreateView(CreateView, LoginRequiredMixin, StaffOrAgencyRequiredMixi
|
|||||||
template_name = "people/create_person.html"
|
template_name = "people/create_person.html"
|
||||||
form_class = PersonForm
|
form_class = PersonForm
|
||||||
success_url = reverse_lazy("person_list")
|
success_url = reverse_lazy("person_list")
|
||||||
print("from agency")
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
if "HX-Request" in self.request.headers:
|
|
||||||
instance = form.save()
|
instance = form.save()
|
||||||
view = self.request.POST.get("view")
|
view = self.request.POST.get("view")
|
||||||
if view == "portal":
|
if view == "portal":
|
||||||
slug = self.request.POST.get("agency")
|
slug = self.request.POST.get("agency")
|
||||||
if slug:
|
if slug:
|
||||||
agency = HiringAgency.objects.get(slug=slug)
|
agency = HiringAgency.objects.get(slug=slug)
|
||||||
print(agency)
|
print(agency)
|
||||||
instance.agency = agency
|
instance.agency = agency
|
||||||
instance.save()
|
instance.save()
|
||||||
return redirect("agency_portal_persons_list")
|
|
||||||
if view == "job":
|
|
||||||
return redirect("application_create")
|
# 2. Add the content to update (e.g., re-render the person list table)
|
||||||
|
# response.content = render_to_string('recruitment/persons_table.html',
|
||||||
|
return redirect("agency_portal_persons_list")
|
||||||
|
if view == "job":
|
||||||
|
return redirect("application_create")
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
@ -3114,7 +3119,7 @@ def agency_portal_persons_list(request):
|
|||||||
| Q(last_name__icontains=search_query)
|
| Q(last_name__icontains=search_query)
|
||||||
| Q(email__icontains=search_query)
|
| Q(email__icontains=search_query)
|
||||||
| Q(phone=search_query)
|
| Q(phone=search_query)
|
||||||
| Q(job__title__icontains=search_query)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
paginator = Paginator(persons, 20) # Show 20 persons per page
|
paginator = Paginator(persons, 20) # Show 20 persons per page
|
||||||
@ -3636,7 +3641,7 @@ def message_create(request):
|
|||||||
|
|
||||||
if message.recipient and message.recipient.email:
|
if message.recipient and message.recipient.email:
|
||||||
if request.user.user_type != "staff":
|
if request.user.user_type != "staff":
|
||||||
message = message.content
|
body = message.content
|
||||||
else:
|
else:
|
||||||
body = (
|
body = (
|
||||||
message.content
|
message.content
|
||||||
@ -5386,19 +5391,19 @@ class ApplicationListView(LoginRequiredMixin, StaffRequiredMixin, ListView):
|
|||||||
template_name = "recruitment/applications_list.html"
|
template_name = "recruitment/applications_list.html"
|
||||||
context_object_name = "applications"
|
context_object_name = "applications"
|
||||||
paginate_by = 100
|
paginate_by = 100
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = (
|
queryset = (
|
||||||
super()
|
super()
|
||||||
.get_queryset()
|
.get_queryset()
|
||||||
.select_related("person", "job")
|
.select_related("person", "job")
|
||||||
.prefetch_related("interview_set")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Handle search
|
# Handle search
|
||||||
search_query = self.request.GET.get("search", "")
|
search_query = self.request.GET.get("search", "")
|
||||||
job = self.request.GET.get("job", "")
|
job = self.request.GET.get("job", "")
|
||||||
stage = self.request.GET.get("stage", "")
|
stage = self.request.GET.get("stage", "")
|
||||||
|
|
||||||
if search_query:
|
if search_query:
|
||||||
queryset = queryset.filter(
|
queryset = queryset.filter(
|
||||||
Q(person__first_name=search_query) |
|
Q(person__first_name=search_query) |
|
||||||
@ -5463,7 +5468,7 @@ class ApplicationCreateView(
|
|||||||
nationalities = cache.get(cache_key)
|
nationalities = cache.get(cache_key)
|
||||||
if nationalities is None:
|
if nationalities is None:
|
||||||
nationalities = list(
|
nationalities = list(
|
||||||
self.model.objects.values_list("nationality", flat=True)
|
Person.objects.values_list("nationality", flat=True)
|
||||||
.filter(nationality__isnull=False)
|
.filter(nationality__isnull=False)
|
||||||
.distinct()
|
.distinct()
|
||||||
.order_by("nationality")
|
.order_by("nationality")
|
||||||
@ -5474,6 +5479,7 @@ class ApplicationCreateView(
|
|||||||
context["nationality"] = nationality
|
context["nationality"] = nationality
|
||||||
context["nationalities"] = nationalities
|
context["nationalities"] = nationalities
|
||||||
context["search_query"] = self.request.GET.get("search", "")
|
context["search_query"] = self.request.GET.get("search", "")
|
||||||
|
context["person_form"]=PersonForm()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -83,6 +83,7 @@
|
|||||||
.bg-ACTIVE { background-color: var(--kaauh-teal) !important; }
|
.bg-ACTIVE { background-color: var(--kaauh-teal) !important; }
|
||||||
.bg-CLOSED { background-color: var(--kaauh-danger) !important; }
|
.bg-CLOSED { background-color: var(--kaauh-danger) !important; }
|
||||||
.bg-ARCHIVED { background-color: #343a40 !important; }
|
.bg-ARCHIVED { background-color: #343a40 !important; }
|
||||||
|
.bg-CANCELLED{background-color: red !important; }
|
||||||
|
|
||||||
/* --- TABLE STYLING --- */
|
/* --- TABLE STYLING --- */
|
||||||
.table {
|
.table {
|
||||||
@ -238,6 +239,7 @@
|
|||||||
<option value="DRAFT" {% if status_filter == 'DRAFT' %}selected{% endif %}>{% trans "Draft" %}</option>
|
<option value="DRAFT" {% if status_filter == 'DRAFT' %}selected{% endif %}>{% trans "Draft" %}</option>
|
||||||
<option value="ACTIVE" {% if status_filter == 'ACTIVE' %}selected{% endif %}>{% trans "Active" %}</option>
|
<option value="ACTIVE" {% if status_filter == 'ACTIVE' %}selected{% endif %}>{% trans "Active" %}</option>
|
||||||
<option value="CLOSED" {% if status_filter == 'CLOSED' %}selected{% endif %}>{% trans "Closed" %}</option>
|
<option value="CLOSED" {% if status_filter == 'CLOSED' %}selected{% endif %}>{% trans "Closed" %}</option>
|
||||||
|
<option value="CANCELLED" {% if status_filter == 'CANCELLED' %}selected{% endif %}>{% trans "Cancelled" %}</option>
|
||||||
<option value="ARCHIVED" {% if status_filter == 'ARCHIVED' %}selected{% endif %}>{% trans "Archived" %}</option>
|
<option value="ARCHIVED" {% if status_filter == 'ARCHIVED' %}selected{% endif %}>{% trans "Archived" %}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -64,7 +64,7 @@
|
|||||||
<div class="col-md-3 mb-2">
|
<div class="col-md-3 mb-2">
|
||||||
<div class="kaauh-card shadow-sm h-100">
|
<div class="kaauh-card shadow-sm h-100">
|
||||||
<div class="card-body text-center px-2 py-2">
|
<div class="card-body text-center px-2 py-2">
|
||||||
<div class="text-primary mb-2">
|
<div class="text-primary-theme mb-2">
|
||||||
<i class="fas fa-briefcase fa-2x"></i>
|
<i class="fas fa-briefcase fa-2x"></i>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">{{ total_assignments }}</h4>
|
<h4 class="card-title">{{ total_assignments }}</h4>
|
||||||
@ -75,7 +75,7 @@
|
|||||||
<div class="col-md-3 mb-2">
|
<div class="col-md-3 mb-2">
|
||||||
<div class="kaauh-card shadow-sm h-100 px-2 py-2">
|
<div class="kaauh-card shadow-sm h-100 px-2 py-2">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<div class="text-success mb-2">
|
<div class="text-primary-theme mb-2">
|
||||||
<i class="fas fa-check-circle fa-2x"></i>
|
<i class="fas fa-check-circle fa-2x"></i>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">{{ active_assignments }}</h4>
|
<h4 class="card-title">{{ active_assignments }}</h4>
|
||||||
@ -86,7 +86,7 @@
|
|||||||
<div class="col-md-3 mb-2">
|
<div class="col-md-3 mb-2">
|
||||||
<div class="kaauh-card shadow-sm h-100 px-2 py-2">
|
<div class="kaauh-card shadow-sm h-100 px-2 py-2">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<div class="text-info mb-2">
|
<div class="text-primary-theme mb-2">
|
||||||
<i class="fas fa-users fa-2x"></i>
|
<i class="fas fa-users fa-2x"></i>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">{{ total_applications }}</h4>
|
<h4 class="card-title">{{ total_applications }}</h4>
|
||||||
@ -166,7 +166,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<small class="text-muted d-block">{% trans "Applications" %}</small>
|
<small class="text-muted d-block">{% trans "Applications" %}</small>
|
||||||
<strong>{{ stats.application_count }} / {{ stats.assignment.max_applications }}</strong>
|
<strong>{{ stats.application_count }} / {{ stats.assignment.max_candidates}}</strong>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -174,7 +174,7 @@
|
|||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<div class="d-flex justify-content-between mb-1">
|
<div class="d-flex justify-content-between mb-1">
|
||||||
<small class="text-muted">{% trans "Submission Progress" %}</small>
|
<small class="text-muted">{% trans "Submission Progress" %}</small>
|
||||||
<small class="text-muted">{{ stats.application_count }}/{{ stats.assignment.max_applications }}</small>
|
<small class="text-muted">{{ stats.application_count }}/{{ stats.assignment.max_candidates }}</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="progress" style="height: 6px;">
|
<div class="progress" style="height: 6px;">
|
||||||
{% with progress=stats.application_count %}
|
{% with progress=stats.application_count %}
|
||||||
|
|||||||
@ -91,7 +91,7 @@
|
|||||||
id="search"
|
id="search"
|
||||||
name="q"
|
name="q"
|
||||||
value="{{ search_query }}"
|
value="{{ search_query }}"
|
||||||
placeholder="{% trans 'Search by name, email, phone, or job title...' %}">
|
placeholder="{% trans 'Search by name, email, phone...' %}">
|
||||||
</div>
|
</div>
|
||||||
{% comment %} <div class="col-md-3">
|
{% comment %} <div class="col-md-3">
|
||||||
<label for="stage" class="form-label fw-semibold">
|
<label for="stage" class="form-label fw-semibold">
|
||||||
@ -122,7 +122,7 @@
|
|||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="kaauh-card shadow-sm h-100 p-3">
|
<div class="kaauh-card shadow-sm h-100 p-3">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<div class="text-info mb-2">
|
<div class="text-primary-theme mb-2">
|
||||||
<i class="fas fa-users fa-2x"></i>
|
<i class="fas fa-users fa-2x"></i>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">{{ total_persons }}</h4>
|
<h4 class="card-title">{{ total_persons }}</h4>
|
||||||
@ -133,7 +133,7 @@
|
|||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<div class="kaauh-card shadow-sm h-100 p-3">
|
<div class="kaauh-card shadow-sm h-100 p-3">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<div class="text-success mb-2">
|
<div class="text-primary-theme mb-2">
|
||||||
<i class="fas fa-check-circle fa-2x"></i>
|
<i class="fas fa-check-circle fa-2x"></i>
|
||||||
</div>
|
</div>
|
||||||
<h4 class="card-title">{{ page_obj|length }}</h4>
|
<h4 class="card-title">{{ page_obj|length }}</h4>
|
||||||
@ -324,10 +324,12 @@
|
|||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body" id="personModalBody">
|
<div class="modal-body" id="personModalBody">
|
||||||
<form id="person_form" hx-post="{% url 'person_create' %}" hx-vals='{"view":"portal","agency":"{{ agency.slug }}"}' hx-select=".persons-list" hx-target=".persons-list" hx-swap="outerHTML"
|
<form id="person_form" method="post" action="{% url 'person_create' %}" >
|
||||||
hx-on:afterRequest="$('#personModal').modal('hide')">
|
|
||||||
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="view" value="portal">
|
||||||
|
<input type="hidden" name="agency" value="{{ agency.slug }}">
|
||||||
|
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
{{ person_form.first_name|as_crispy_field }}
|
{{ person_form.first_name|as_crispy_field }}
|
||||||
@ -342,6 +344,7 @@
|
|||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{{ person_form.email|as_crispy_field }}
|
{{ person_form.email|as_crispy_field }}
|
||||||
|
{{person_form.errors}}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{{ person_form.phone|as_crispy_field }}
|
{{ person_form.phone|as_crispy_field }}
|
||||||
|
|||||||
@ -98,7 +98,7 @@
|
|||||||
<div class="d-flex gap-2 mt-1">
|
<div class="d-flex gap-2 mt-1">
|
||||||
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#personModal">
|
<button type="button" class="btn btn-outline-secondary btn-sm" data-bs-toggle="modal" data-bs-target="#personModal">
|
||||||
<i class="fas fa-user-plus me-1"></i>
|
<i class="fas fa-user-plus me-1"></i>
|
||||||
<span class="d-none d-sm-inline">{% trans "Create New Person" %}</span>
|
<span class="d-none d-sm-inline">{% trans "Create New Applicant" %}</span>
|
||||||
</button>
|
</button>
|
||||||
<a href="{% url 'application_list' %}" class="btn btn-outline-light btn-sm" title="{% trans 'Back to List' %}">
|
<a href="{% url 'application_list' %}" class="btn btn-outline-light btn-sm" title="{% trans 'Back to List' %}">
|
||||||
<i class="fas fa-arrow-left"></i>
|
<i class="fas fa-arrow-left"></i>
|
||||||
@ -171,6 +171,14 @@
|
|||||||
{{ person_form.phone|as_crispy_field }}
|
{{ person_form.phone|as_crispy_field }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="row g-4">
|
||||||
|
<div class="col-md-6">
|
||||||
|
{{ person_form.gpa|as_crispy_field }}
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
{{ person_form.national_id|as_crispy_field }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
{{ person_form.date_of_birth|as_crispy_field }}
|
{{ person_form.date_of_birth|as_crispy_field }}
|
||||||
|
|||||||
@ -5,6 +5,15 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'source_list' %}" class="text-decoration-none text-secondary">{% trans "Souce Settings" %}</a></li>
|
||||||
|
<li class="breadcrumb-item active" aria-current="page" style="
|
||||||
|
color: #F43B5E; /* Rosy Accent Color */
|
||||||
|
font-weight: 600;
|
||||||
|
">{% trans "Source Detail" %}</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
|||||||
@ -45,7 +45,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<form method="post" class="space-y-4">
|
<form method="post" class="space-y-4">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
|
|
||||||
|
|||||||
@ -112,7 +112,13 @@
|
|||||||
<p class="text-muted mb-0">{% trans "Manage your personal details and security." %}</p>
|
<p class="text-muted mb-0">{% trans "Manage your personal details and security." %}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="rounded-circle bg-primary-subtle text-accent d-flex align-items-center justify-content-center" style="width: 50px; height: 50px; font-size: 1.5rem;">
|
<div class="rounded-circle bg-primary-subtle text-accent d-flex align-items-center justify-content-center" style="width: 50px; height: 50px; font-size: 1.5rem;">
|
||||||
{% if user.first_name %}{{ user.first_name.0 }}{% else %}<i class="fas fa-user"></i>{% endif %}
|
{% if user.profile_image %}
|
||||||
|
<img src="{{ user.profile_image.url }}" alt="{{ user.username }}" class="profile-avatar"
|
||||||
|
style="width: 100px; height: 100px; object-fit: cover; background-color: var(--kaauh-teal); display: inline-block; vertical-align: middle;"
|
||||||
|
title="{% trans 'Your account' %}">
|
||||||
|
{% else %}
|
||||||
|
{% if user.first_name %}{{ user.first_name.0 }}{% else %}<i class="fas fa-user"></i>{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user