add login to candidate and agency
This commit is contained in:
parent
9830b1173f
commit
caa7ed88aa
@ -9,6 +9,7 @@ https://docs.djangoproject.com/en/5.2/topics/settings/
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.2/ref/settings/
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from django.templatetags.static import static
|
||||
@ -20,7 +21,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-_!ew&)1&r--3h17knd27^x8(xu(&-f4q3%x543lv5vx2!784s*'
|
||||
SECRET_KEY = "django-insecure-_!ew&)1&r--3h17knd27^x8(xu(&-f4q3%x543lv5vx2!784s*"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
@ -30,116 +31,114 @@ ALLOWED_HOSTS = ["*"]
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.humanize',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'rest_framework',
|
||||
'recruitment.apps.RecruitmentConfig',
|
||||
'corsheaders',
|
||||
'django.contrib.sites',
|
||||
'allauth',
|
||||
'allauth.account',
|
||||
'allauth.socialaccount',
|
||||
'allauth.socialaccount.providers.linkedin_oauth2',
|
||||
'channels',
|
||||
'django_filters',
|
||||
'crispy_forms',
|
||||
"django.contrib.admin",
|
||||
"django.contrib.humanize",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"rest_framework",
|
||||
"recruitment.apps.RecruitmentConfig",
|
||||
"corsheaders",
|
||||
"django.contrib.sites",
|
||||
"allauth",
|
||||
"allauth.account",
|
||||
"allauth.socialaccount",
|
||||
"allauth.socialaccount.providers.linkedin_oauth2",
|
||||
"channels",
|
||||
"django_filters",
|
||||
"crispy_forms",
|
||||
# 'django_summernote',
|
||||
# 'ckeditor',
|
||||
'django_ckeditor_5',
|
||||
'crispy_bootstrap5',
|
||||
'django_extensions',
|
||||
'template_partials',
|
||||
'django_countries',
|
||||
'django_celery_results',
|
||||
'django_q',
|
||||
'widget_tweaks',
|
||||
'easyaudit'
|
||||
"django_ckeditor_5",
|
||||
"crispy_bootstrap5",
|
||||
"django_extensions",
|
||||
"template_partials",
|
||||
"django_countries",
|
||||
"django_celery_results",
|
||||
"django_q",
|
||||
"widget_tweaks",
|
||||
"easyaudit",
|
||||
]
|
||||
|
||||
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
|
||||
LOGIN_REDIRECT_URL = 'dashboard'
|
||||
LOGIN_REDIRECT_URL = "dashboard"
|
||||
|
||||
|
||||
ACCOUNT_LOGOUT_REDIRECT_URL = '/'
|
||||
ACCOUNT_LOGOUT_REDIRECT_URL = "/"
|
||||
|
||||
|
||||
ACCOUNT_SIGNUP_REDIRECT_URL = '/'
|
||||
ACCOUNT_SIGNUP_REDIRECT_URL = "/"
|
||||
|
||||
|
||||
LOGIN_URL = '/accounts/login/'
|
||||
|
||||
LOGIN_URL = "/accounts/login/"
|
||||
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'allauth.account.auth_backends.AuthenticationBackend',
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
"allauth.account.auth_backends.AuthenticationBackend",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'allauth.account.middleware.AccountMiddleware',
|
||||
'easyaudit.middleware.easyaudit.EasyAuditMiddleware',
|
||||
"corsheaders.middleware.CorsMiddleware",
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"allauth.account.middleware.AccountMiddleware",
|
||||
"easyaudit.middleware.easyaudit.EasyAuditMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'NorahUniversity.urls'
|
||||
ROOT_URLCONF = "NorahUniversity.urls"
|
||||
|
||||
CORS_ALLOW_ALL_ORIGINS = True
|
||||
|
||||
ASGI_APPLICATION = 'hospital_recruitment.asgi.application'
|
||||
ASGI_APPLICATION = "hospital_recruitment.asgi.application"
|
||||
CHANNEL_LAYERS = {
|
||||
'default': {
|
||||
'BACKEND': 'channels_redis.core.RedisChannelLayer',
|
||||
'CONFIG': {
|
||||
'hosts': [('127.0.0.1', 6379)],
|
||||
"default": {
|
||||
"BACKEND": "channels_redis.core.RedisChannelLayer",
|
||||
"CONFIG": {
|
||||
"hosts": [("127.0.0.1", 6379)],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(BASE_DIR, 'templates')],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [os.path.join(BASE_DIR, "templates")],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'NorahUniversity.wsgi.application'
|
||||
WSGI_APPLICATION = "NorahUniversity.wsgi.application"
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
'NAME': 'haikal_db',
|
||||
'USER': 'faheed',
|
||||
'PASSWORD': 'Faheed@215',
|
||||
'HOST': '127.0.0.1',
|
||||
'PORT': '5432',
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql_psycopg2",
|
||||
"NAME": "norahuniversity",
|
||||
"USER": "norahuniversity",
|
||||
"PASSWORD": "norahuniversity",
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": "5432",
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,7 +153,6 @@ DATABASES = {
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
|
||||
|
||||
|
||||
|
||||
# AUTH_PASSWORD_VALIDATORS = [
|
||||
# {
|
||||
# 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
@ -174,13 +172,13 @@ DATABASES = {
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
ACCOUNT_LOGIN_METHODS = ['email']
|
||||
ACCOUNT_SIGNUP_FIELDS = ['email*', 'password1*', 'password2*']
|
||||
ACCOUNT_LOGIN_METHODS = ["email"]
|
||||
ACCOUNT_SIGNUP_FIELDS = ["email*", "password1*", "password2*"]
|
||||
|
||||
ACCOUNT_UNIQUE_EMAIL = True
|
||||
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
|
||||
@ -188,10 +186,10 @@ ACCOUNT_EMAIL_VERIFICATION = "mandatory"
|
||||
ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True
|
||||
|
||||
|
||||
ACCOUNT_FORMS = {'signup': 'recruitment.forms.StaffSignupForm'}
|
||||
ACCOUNT_FORMS = {"signup": "recruitment.forms.StaffSignupForm"}
|
||||
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
|
||||
|
||||
# Crispy Forms Configuration
|
||||
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||
@ -199,29 +197,29 @@ CRISPY_TEMPLATE_PACK = "bootstrapconsole5"
|
||||
|
||||
# Bootstrap 5 Configuration
|
||||
CRISPY_BS5 = {
|
||||
'include_placeholder_text': True,
|
||||
'use_css_helpers': True,
|
||||
"include_placeholder_text": True,
|
||||
"use_css_helpers": True,
|
||||
}
|
||||
|
||||
ACCOUNT_RATE_LIMITS = {
|
||||
'send_email_confirmation': None, # Disables the limit
|
||||
"send_email_confirmation": None, # Disables the limit
|
||||
}
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/5.2/topics/i18n/
|
||||
|
||||
LANGUAGES = [
|
||||
('en', 'English'),
|
||||
('ar', 'Arabic'),
|
||||
("en", "English"),
|
||||
("ar", "Arabic"),
|
||||
]
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
LOCALE_PATHS = [
|
||||
BASE_DIR / 'locale',
|
||||
BASE_DIR / "locale",
|
||||
]
|
||||
|
||||
TIME_ZONE = 'Asia/Riyadh'
|
||||
TIME_ZONE = "Asia/Riyadh"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
@ -230,36 +228,35 @@ USE_TZ = True
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/5.2/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
MEDIA_URL = '/media/'
|
||||
STATICFILES_DIRS = [
|
||||
BASE_DIR / 'static'
|
||||
]
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
STATIC_URL = "/static/"
|
||||
MEDIA_URL = "/media/"
|
||||
STATICFILES_DIRS = [BASE_DIR / "static"]
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
|
||||
# LinkedIn OAuth Config
|
||||
SOCIALACCOUNT_PROVIDERS = {
|
||||
'linkedin_oauth2': {
|
||||
'SCOPE': [
|
||||
'r_liteprofile', 'r_emailaddress', 'w_member_social',
|
||||
'rw_organization_admin', 'w_organization_social'
|
||||
"linkedin_oauth2": {
|
||||
"SCOPE": [
|
||||
"r_liteprofile",
|
||||
"r_emailaddress",
|
||||
"w_member_social",
|
||||
"rw_organization_admin",
|
||||
"w_organization_social",
|
||||
],
|
||||
'PROFILE_FIELDS': [
|
||||
'id', 'first-name', 'last-name', 'email-address'
|
||||
]
|
||||
"PROFILE_FIELDS": ["id", "first-name", "last-name", "email-address"],
|
||||
}
|
||||
}
|
||||
|
||||
ZOOM_ACCOUNT_ID = 'HoGikHXsQB2GNDC5Rvyw9A'
|
||||
ZOOM_CLIENT_ID = 'brC39920R8C8azfudUaQgA'
|
||||
ZOOM_CLIENT_SECRET = 'rvfhjlbID4ychXPOvZ2lYsoAC0B0Ny2L'
|
||||
SECRET_TOKEN = '6KdTGyF0SSCSL_V4Xa34aw'
|
||||
ZOOM_ACCOUNT_ID = "HoGikHXsQB2GNDC5Rvyw9A"
|
||||
ZOOM_CLIENT_ID = "brC39920R8C8azfudUaQgA"
|
||||
ZOOM_CLIENT_SECRET = "rvfhjlbID4ychXPOvZ2lYsoAC0B0Ny2L"
|
||||
SECRET_TOKEN = "6KdTGyF0SSCSL_V4Xa34aw"
|
||||
ZOOM_WEBHOOK_API_KEY = "2GNDC5Rvyw9AHoGikHXsQB"
|
||||
|
||||
# Maximum file upload size (in bytes)
|
||||
@ -268,142 +265,200 @@ FILE_UPLOAD_MAX_MEMORY_SIZE = 10485760 # 10MB
|
||||
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
|
||||
CELERY_BROKER_URL = 'redis://localhost:6379/0' # Or your message broker URL
|
||||
CELERY_RESULT_BACKEND = 'django-db' # If using django-celery-results
|
||||
CELERY_ACCEPT_CONTENT = ['application/json']
|
||||
CELERY_TASK_SERIALIZER = 'json'
|
||||
CELERY_RESULT_SERIALIZER = 'json'
|
||||
CELERY_TIMEZONE = 'UTC'
|
||||
CELERY_BROKER_URL = "redis://localhost:6379/0" # Or your message broker URL
|
||||
CELERY_RESULT_BACKEND = "django-db" # If using django-celery-results
|
||||
CELERY_ACCEPT_CONTENT = ["application/json"]
|
||||
CELERY_TASK_SERIALIZER = "json"
|
||||
CELERY_RESULT_SERIALIZER = "json"
|
||||
CELERY_TIMEZONE = "UTC"
|
||||
|
||||
LINKEDIN_CLIENT_ID = '867jwsiyem1504'
|
||||
LINKEDIN_CLIENT_SECRET = 'WPL_AP1.QNH5lYnfRSQpp0Qp.GO8Srw=='
|
||||
LINKEDIN_REDIRECT_URI = 'http://127.0.0.1:8000/jobs/linkedin/callback/'
|
||||
LINKEDIN_CLIENT_ID = "867jwsiyem1504"
|
||||
LINKEDIN_CLIENT_SECRET = "WPL_AP1.QNH5lYnfRSQpp0Qp.GO8Srw=="
|
||||
LINKEDIN_REDIRECT_URI = "http://127.0.0.1:8000/jobs/linkedin/callback/"
|
||||
|
||||
|
||||
Q_CLUSTER = {
|
||||
'name': 'KAAUH_CLUSTER',
|
||||
'workers': 8,
|
||||
'recycle': 500,
|
||||
'timeout': 60,
|
||||
'max_attempts': 1,
|
||||
'compress': True,
|
||||
'save_limit': 250,
|
||||
'queue_limit': 500,
|
||||
'cpu_affinity': 1,
|
||||
'label': 'Django Q2',
|
||||
'redis': {
|
||||
'host': '127.0.0.1',
|
||||
'port': 6379,
|
||||
'db': 3, },
|
||||
'ALT_CLUSTERS': {
|
||||
'long': {
|
||||
'timeout': 3000,
|
||||
'retry': 3600,
|
||||
'max_attempts': 2,
|
||||
"name": "KAAUH_CLUSTER",
|
||||
"workers": 8,
|
||||
"recycle": 500,
|
||||
"timeout": 60,
|
||||
"max_attempts": 1,
|
||||
"compress": True,
|
||||
"save_limit": 250,
|
||||
"queue_limit": 500,
|
||||
"cpu_affinity": 1,
|
||||
"label": "Django Q2",
|
||||
"redis": {
|
||||
"host": "127.0.0.1",
|
||||
"port": 6379,
|
||||
"db": 3,
|
||||
},
|
||||
"ALT_CLUSTERS": {
|
||||
"long": {
|
||||
"timeout": 3000,
|
||||
"retry": 3600,
|
||||
"max_attempts": 2,
|
||||
},
|
||||
'short': {
|
||||
'timeout': 10,
|
||||
'max_attempts': 1,
|
||||
"short": {
|
||||
"timeout": 10,
|
||||
"max_attempts": 1,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
customColorPalette = [
|
||||
{
|
||||
'color': 'hsl(4, 90%, 58%)',
|
||||
'label': 'Red'
|
||||
},
|
||||
{
|
||||
'color': 'hsl(340, 82%, 52%)',
|
||||
'label': 'Pink'
|
||||
},
|
||||
{
|
||||
'color': 'hsl(291, 64%, 42%)',
|
||||
'label': 'Purple'
|
||||
},
|
||||
{
|
||||
'color': 'hsl(262, 52%, 47%)',
|
||||
'label': 'Deep Purple'
|
||||
},
|
||||
{
|
||||
'color': 'hsl(231, 48%, 48%)',
|
||||
'label': 'Indigo'
|
||||
},
|
||||
{
|
||||
'color': 'hsl(207, 90%, 54%)',
|
||||
'label': 'Blue'
|
||||
},
|
||||
]
|
||||
{"color": "hsl(4, 90%, 58%)", "label": "Red"},
|
||||
{"color": "hsl(340, 82%, 52%)", "label": "Pink"},
|
||||
{"color": "hsl(291, 64%, 42%)", "label": "Purple"},
|
||||
{"color": "hsl(262, 52%, 47%)", "label": "Deep Purple"},
|
||||
{"color": "hsl(231, 48%, 48%)", "label": "Indigo"},
|
||||
{"color": "hsl(207, 90%, 54%)", "label": "Blue"},
|
||||
]
|
||||
|
||||
# CKEDITOR_5_CUSTOM_CSS = 'path_to.css' # optional
|
||||
# CKEDITOR_5_FILE_STORAGE = "path_to_storage.CustomStorage" # optional
|
||||
CKEDITOR_5_CONFIGS = {
|
||||
'default': {
|
||||
'toolbar': {
|
||||
'items': ['heading', '|', 'bold', 'italic', 'link',
|
||||
'bulletedList', 'numberedList', 'blockQuote', 'imageUpload', ],
|
||||
}
|
||||
|
||||
"default": {
|
||||
"toolbar": {
|
||||
"items": [
|
||||
"heading",
|
||||
"|",
|
||||
"bold",
|
||||
"italic",
|
||||
"link",
|
||||
"bulletedList",
|
||||
"numberedList",
|
||||
"blockQuote",
|
||||
"imageUpload",
|
||||
],
|
||||
}
|
||||
},
|
||||
'extends': {
|
||||
'blockToolbar': [
|
||||
'paragraph', 'heading1', 'heading2', 'heading3',
|
||||
'|',
|
||||
'bulletedList', 'numberedList',
|
||||
'|',
|
||||
'blockQuote',
|
||||
"extends": {
|
||||
"blockToolbar": [
|
||||
"paragraph",
|
||||
"heading1",
|
||||
"heading2",
|
||||
"heading3",
|
||||
"|",
|
||||
"bulletedList",
|
||||
"numberedList",
|
||||
"|",
|
||||
"blockQuote",
|
||||
],
|
||||
'toolbar': {
|
||||
'items': ['heading', '|', 'outdent', 'indent', '|', 'bold', 'italic', 'link', 'underline', 'strikethrough',
|
||||
'code','subscript', 'superscript', 'highlight', '|', 'codeBlock', 'sourceEditing', 'insertImage',
|
||||
'bulletedList', 'numberedList', 'todoList', '|', 'blockQuote', 'imageUpload', '|',
|
||||
'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor', 'mediaEmbed', 'removeFormat',
|
||||
'insertTable',
|
||||
],
|
||||
'shouldNotGroupWhenFull': 'true'
|
||||
"toolbar": {
|
||||
"items": [
|
||||
"heading",
|
||||
"|",
|
||||
"outdent",
|
||||
"indent",
|
||||
"|",
|
||||
"bold",
|
||||
"italic",
|
||||
"link",
|
||||
"underline",
|
||||
"strikethrough",
|
||||
"code",
|
||||
"subscript",
|
||||
"superscript",
|
||||
"highlight",
|
||||
"|",
|
||||
"codeBlock",
|
||||
"sourceEditing",
|
||||
"insertImage",
|
||||
"bulletedList",
|
||||
"numberedList",
|
||||
"todoList",
|
||||
"|",
|
||||
"blockQuote",
|
||||
"imageUpload",
|
||||
"|",
|
||||
"fontSize",
|
||||
"fontFamily",
|
||||
"fontColor",
|
||||
"fontBackgroundColor",
|
||||
"mediaEmbed",
|
||||
"removeFormat",
|
||||
"insertTable",
|
||||
],
|
||||
"shouldNotGroupWhenFull": "true",
|
||||
},
|
||||
'image': {
|
||||
'toolbar': ['imageTextAlternative', '|', 'imageStyle:alignLeft',
|
||||
'imageStyle:alignRight', 'imageStyle:alignCenter', 'imageStyle:side', '|'],
|
||||
'styles': [
|
||||
'full',
|
||||
'side',
|
||||
'alignLeft',
|
||||
'alignRight',
|
||||
'alignCenter',
|
||||
]
|
||||
|
||||
"image": {
|
||||
"toolbar": [
|
||||
"imageTextAlternative",
|
||||
"|",
|
||||
"imageStyle:alignLeft",
|
||||
"imageStyle:alignRight",
|
||||
"imageStyle:alignCenter",
|
||||
"imageStyle:side",
|
||||
"|",
|
||||
],
|
||||
"styles": [
|
||||
"full",
|
||||
"side",
|
||||
"alignLeft",
|
||||
"alignRight",
|
||||
"alignCenter",
|
||||
],
|
||||
},
|
||||
'table': {
|
||||
'contentToolbar': [ 'tableColumn', 'tableRow', 'mergeTableCells',
|
||||
'tableProperties', 'tableCellProperties' ],
|
||||
'tableProperties': {
|
||||
'borderColors': customColorPalette,
|
||||
'backgroundColors': customColorPalette
|
||||
"table": {
|
||||
"contentToolbar": [
|
||||
"tableColumn",
|
||||
"tableRow",
|
||||
"mergeTableCells",
|
||||
"tableProperties",
|
||||
"tableCellProperties",
|
||||
],
|
||||
"tableProperties": {
|
||||
"borderColors": customColorPalette,
|
||||
"backgroundColors": customColorPalette,
|
||||
},
|
||||
"tableCellProperties": {
|
||||
"borderColors": customColorPalette,
|
||||
"backgroundColors": customColorPalette,
|
||||
},
|
||||
'tableCellProperties': {
|
||||
'borderColors': customColorPalette,
|
||||
'backgroundColors': customColorPalette
|
||||
}
|
||||
},
|
||||
'heading' : {
|
||||
'options': [
|
||||
{ 'model': 'paragraph', 'title': 'Paragraph', 'class': 'ck-heading_paragraph' },
|
||||
{ 'model': 'heading1', 'view': 'h1', 'title': 'Heading 1', 'class': 'ck-heading_heading1' },
|
||||
{ 'model': 'heading2', 'view': 'h2', 'title': 'Heading 2', 'class': 'ck-heading_heading2' },
|
||||
{ 'model': 'heading3', 'view': 'h3', 'title': 'Heading 3', 'class': 'ck-heading_heading3' }
|
||||
"heading": {
|
||||
"options": [
|
||||
{
|
||||
"model": "paragraph",
|
||||
"title": "Paragraph",
|
||||
"class": "ck-heading_paragraph",
|
||||
},
|
||||
{
|
||||
"model": "heading1",
|
||||
"view": "h1",
|
||||
"title": "Heading 1",
|
||||
"class": "ck-heading_heading1",
|
||||
},
|
||||
{
|
||||
"model": "heading2",
|
||||
"view": "h2",
|
||||
"title": "Heading 2",
|
||||
"class": "ck-heading_heading2",
|
||||
},
|
||||
{
|
||||
"model": "heading3",
|
||||
"view": "h3",
|
||||
"title": "Heading 3",
|
||||
"class": "ck-heading_heading3",
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
"list": {
|
||||
"properties": {
|
||||
"styles": "true",
|
||||
"startIndex": "true",
|
||||
"reversed": "true",
|
||||
}
|
||||
},
|
||||
'list': {
|
||||
'properties': {
|
||||
'styles': 'true',
|
||||
'startIndex': 'true',
|
||||
'reversed': 'true',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Define a constant in settings.py to specify file upload permissions
|
||||
CKEDITOR_5_FILE_UPLOAD_PERMISSION = "staff" # Possible values: "staff", "authenticated", "any"
|
||||
CKEDITOR_5_FILE_UPLOAD_PERMISSION = (
|
||||
"staff" # Possible values: "staff", "authenticated", "any"
|
||||
)
|
||||
|
||||
# Custom User Model
|
||||
AUTH_USER_MODEL = "recruitment.CustomUser"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -8,7 +8,9 @@ from .models import (
|
||||
SharedFormTemplate, Source, HiringAgency, IntegrationLog,InterviewSchedule,Profile,JobPostingImage,MeetingComment,
|
||||
AgencyAccessLink, AgencyJobAssignment
|
||||
)
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
User = get_user_model()
|
||||
class FormFieldInline(admin.TabularInline):
|
||||
model = FormField
|
||||
extra = 1
|
||||
@ -82,17 +84,10 @@ class HiringAgencyAdmin(admin.ModelAdmin):
|
||||
readonly_fields = ['slug', 'created_at', 'updated_at']
|
||||
fieldsets = (
|
||||
('Basic Information', {
|
||||
'fields': ('name', 'slug', 'contact_person', 'email', 'phone', 'website')
|
||||
}),
|
||||
('Location Details', {
|
||||
'fields': ('country', 'city', 'address')
|
||||
}),
|
||||
('Additional Information', {
|
||||
'fields': ('description', 'created_at', 'updated_at')
|
||||
'fields': ('name','contact_person', 'email', 'phone', 'website','user')
|
||||
}),
|
||||
)
|
||||
save_on_top = True
|
||||
prepopulated_fields = {'slug': ('name',)}
|
||||
|
||||
|
||||
@admin.register(JobPosting)
|
||||
@ -151,7 +146,7 @@ class CandidateAdmin(admin.ModelAdmin):
|
||||
readonly_fields = ['slug', 'created_at', 'updated_at']
|
||||
fieldsets = (
|
||||
('Personal Information', {
|
||||
'fields': ('first_name', 'last_name', 'email', 'phone', 'resume')
|
||||
'fields': ('first_name', 'last_name', 'email', 'phone', 'resume','user')
|
||||
}),
|
||||
('Application Details', {
|
||||
'fields': ('job', 'applied', 'stage','is_resume_parsed')
|
||||
@ -163,7 +158,7 @@ class CandidateAdmin(admin.ModelAdmin):
|
||||
'fields': ('ai_analysis_data',)
|
||||
}),
|
||||
('Additional Information', {
|
||||
'fields': ('submitted_by_agency', 'created_at', 'updated_at')
|
||||
'fields': ('created_at', 'updated_at')
|
||||
}),
|
||||
)
|
||||
save_on_top = True
|
||||
@ -290,3 +285,4 @@ admin.site.register(AgencyJobAssignment)
|
||||
|
||||
|
||||
admin.site.register(JobPostingImage)
|
||||
admin.site.register(User)
|
||||
|
||||
1520
recruitment/forms.py
1520
recruitment/forms.py
File diff suppressed because it is too large
Load Diff
Binary file not shown.
38
recruitment/migrations/0003_auto_20251105_1616.py
Normal file
38
recruitment/migrations/0003_auto_20251105_1616.py
Normal file
@ -0,0 +1,38 @@
|
||||
# Generated migration for adding user relationships
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("recruitment", "0002_alter_jobposting_job_type_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="candidate",
|
||||
name="user",
|
||||
field=models.OneToOneField(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="candidate_profile",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="User",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="hiringagency",
|
||||
name="user",
|
||||
field=models.OneToOneField(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="agency_profile",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="User",
|
||||
),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.6 on 2025-11-05 13:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('recruitment', '0003_auto_20251105_1616'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='candidate',
|
||||
name='ai_analysis_data',
|
||||
field=models.JSONField(blank=True, default=dict, help_text='Full JSON output from the resume scoring model.', null=True, verbose_name='AI Analysis Data'),
|
||||
),
|
||||
]
|
||||
44
recruitment/migrations/0005_customuser.py
Normal file
44
recruitment/migrations/0005_customuser.py
Normal file
@ -0,0 +1,44 @@
|
||||
# Generated by Django 5.2.6 on 2025-11-05 13:37
|
||||
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
('recruitment', '0004_alter_candidate_ai_analysis_data'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CustomUser',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('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')),
|
||||
('user_type', models.CharField(choices=[('staff', 'Staff'), ('agency', 'Agency'), ('candidate', 'Candidate')], default='staff', max_length=20, verbose_name='User Type')),
|
||||
('phone', models.CharField(blank=True, max_length=20, null=True, verbose_name='Phone')),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'User',
|
||||
'verbose_name_plural': 'Users',
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@ -5,98 +5,264 @@ from . import views_integration
|
||||
from . import views_source
|
||||
|
||||
urlpatterns = [
|
||||
path('dashboard/', views_frontend.dashboard_view, name='dashboard'),
|
||||
|
||||
path("", views_frontend.dashboard_view, name="dashboard"),
|
||||
# Job URLs (using JobPosting model)
|
||||
path('jobs/', views_frontend.JobListView.as_view(), name='job_list'),
|
||||
path('jobs/create/', views.create_job, name='job_create'),
|
||||
path('job/<slug:slug>/upload_image_simple/', views.job_image_upload, name='job_image_upload'),
|
||||
path('jobs/<slug:slug>/update/', views.edit_job, name='job_update'),
|
||||
path("jobs/", views_frontend.JobListView.as_view(), name="job_list"),
|
||||
path("jobs/create/", views.create_job, name="job_create"),
|
||||
path(
|
||||
"job/<slug:slug>/upload_image_simple/",
|
||||
views.job_image_upload,
|
||||
name="job_image_upload",
|
||||
),
|
||||
path("jobs/<slug:slug>/update/", views.edit_job, name="job_update"),
|
||||
# path('jobs/<slug:slug>/delete/', views., name='job_delete'),
|
||||
path('jobs/<slug:slug>/', views.job_detail, name='job_detail'),
|
||||
|
||||
path('careers/',views.kaauh_career,name='kaauh_career'),
|
||||
|
||||
path("jobs/<slug:slug>/", views.job_detail, name="job_detail"),
|
||||
path("careers/", views.kaauh_career, name="kaauh_career"),
|
||||
# LinkedIn Integration URLs
|
||||
path('jobs/<slug:slug>/post-to-linkedin/', views.post_to_linkedin, name='post_to_linkedin'),
|
||||
path('jobs/linkedin/login/', views.linkedin_login, name='linkedin_login'),
|
||||
path('jobs/linkedin/callback/', views.linkedin_callback, name='linkedin_callback'),
|
||||
|
||||
path('jobs/<slug:slug>/schedule-interviews/', views.schedule_interviews_view, name='schedule_interviews'),
|
||||
path('jobs/<slug:slug>/confirm-schedule-interviews/', views.confirm_schedule_interviews_view, name='confirm_schedule_interviews_view'),
|
||||
path(
|
||||
"jobs/<slug:slug>/post-to-linkedin/",
|
||||
views.post_to_linkedin,
|
||||
name="post_to_linkedin",
|
||||
),
|
||||
path("jobs/linkedin/login/", views.linkedin_login, name="linkedin_login"),
|
||||
path("jobs/linkedin/callback/", views.linkedin_callback, name="linkedin_callback"),
|
||||
path(
|
||||
"jobs/<slug:slug>/schedule-interviews/",
|
||||
views.schedule_interviews_view,
|
||||
name="schedule_interviews",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/confirm-schedule-interviews/",
|
||||
views.confirm_schedule_interviews_view,
|
||||
name="confirm_schedule_interviews_view",
|
||||
),
|
||||
# Candidate URLs
|
||||
path('candidates/', views_frontend.CandidateListView.as_view(), name='candidate_list'),
|
||||
path('candidates/create/', views_frontend.CandidateCreateView.as_view(), name='candidate_create'),
|
||||
path('candidates/create/<slug:slug>/', views_frontend.CandidateCreateView.as_view(), name='candidate_create_for_job'),
|
||||
path('jobs/<slug:slug>/candidates/', views_frontend.JobCandidatesListView.as_view(), name='job_candidates_list'),
|
||||
path('candidates/<slug:slug>/update/', views_frontend.CandidateUpdateView.as_view(), name='candidate_update'),
|
||||
path('candidates/<slug:slug>/delete/', views_frontend.CandidateDeleteView.as_view(), name='candidate_delete'),
|
||||
path('candidate/<slug:slug>/view/', views_frontend.candidate_detail, name='candidate_detail'),
|
||||
path('candidate/<slug:slug>/resume-template/', views_frontend.candidate_resume_template_view, name='candidate_resume_template'),
|
||||
path('candidate/<slug:slug>/update-stage/', views_frontend.candidate_update_stage, name='candidate_update_stage'),
|
||||
path('candidate/<slug:slug>/retry-scoring/', views_frontend.retry_scoring_view, name='candidate_retry_scoring'),
|
||||
|
||||
path(
|
||||
"candidates/", views_frontend.CandidateListView.as_view(), name="candidate_list"
|
||||
),
|
||||
path(
|
||||
"candidates/create/",
|
||||
views_frontend.CandidateCreateView.as_view(),
|
||||
name="candidate_create",
|
||||
),
|
||||
path(
|
||||
"candidates/create/<slug:slug>/",
|
||||
views_frontend.CandidateCreateView.as_view(),
|
||||
name="candidate_create_for_job",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/candidates/",
|
||||
views_frontend.JobCandidatesListView.as_view(),
|
||||
name="job_candidates_list",
|
||||
),
|
||||
path(
|
||||
"candidates/<slug:slug>/update/",
|
||||
views_frontend.CandidateUpdateView.as_view(),
|
||||
name="candidate_update",
|
||||
),
|
||||
path(
|
||||
"candidates/<slug:slug>/delete/",
|
||||
views_frontend.CandidateDeleteView.as_view(),
|
||||
name="candidate_delete",
|
||||
),
|
||||
path(
|
||||
"candidate/<slug:slug>/view/",
|
||||
views_frontend.candidate_detail,
|
||||
name="candidate_detail",
|
||||
),
|
||||
path(
|
||||
"candidate/<slug:slug>/resume-template/",
|
||||
views_frontend.candidate_resume_template_view,
|
||||
name="candidate_resume_template",
|
||||
),
|
||||
path(
|
||||
"candidate/<slug:slug>/update-stage/",
|
||||
views_frontend.candidate_update_stage,
|
||||
name="candidate_update_stage",
|
||||
),
|
||||
path(
|
||||
"candidate/<slug:slug>/retry-scoring/",
|
||||
views_frontend.retry_scoring_view,
|
||||
name="candidate_retry_scoring",
|
||||
),
|
||||
# Training URLs
|
||||
path('training/', views_frontend.TrainingListView.as_view(), name='training_list'),
|
||||
path('training/create/', views_frontend.TrainingCreateView.as_view(), name='training_create'),
|
||||
path('training/<slug:slug>/', views_frontend.TrainingDetailView.as_view(), name='training_detail'),
|
||||
path('training/<slug:slug>/update/', views_frontend.TrainingUpdateView.as_view(), name='training_update'),
|
||||
path('training/<slug:slug>/delete/', views_frontend.TrainingDeleteView.as_view(), name='training_delete'),
|
||||
|
||||
path("training/", views_frontend.TrainingListView.as_view(), name="training_list"),
|
||||
path(
|
||||
"training/create/",
|
||||
views_frontend.TrainingCreateView.as_view(),
|
||||
name="training_create",
|
||||
),
|
||||
path(
|
||||
"training/<slug:slug>/",
|
||||
views_frontend.TrainingDetailView.as_view(),
|
||||
name="training_detail",
|
||||
),
|
||||
path(
|
||||
"training/<slug:slug>/update/",
|
||||
views_frontend.TrainingUpdateView.as_view(),
|
||||
name="training_update",
|
||||
),
|
||||
path(
|
||||
"training/<slug:slug>/delete/",
|
||||
views_frontend.TrainingDeleteView.as_view(),
|
||||
name="training_delete",
|
||||
),
|
||||
# Meeting URLs
|
||||
path('meetings/', views.ZoomMeetingListView.as_view(), name='list_meetings'),
|
||||
path('meetings/create-meeting/', views.ZoomMeetingCreateView.as_view(), name='create_meeting'),
|
||||
path('meetings/meeting-details/<slug:slug>/', views.ZoomMeetingDetailsView.as_view(), name='meeting_details'),
|
||||
path('meetings/update-meeting/<slug:slug>/', views.ZoomMeetingUpdateView.as_view(), name='update_meeting'),
|
||||
path('meetings/delete-meeting/<slug:slug>/', views.ZoomMeetingDeleteView, name='delete_meeting'),
|
||||
|
||||
path("meetings/", views.ZoomMeetingListView.as_view(), name="list_meetings"),
|
||||
path(
|
||||
"meetings/create-meeting/",
|
||||
views.ZoomMeetingCreateView.as_view(),
|
||||
name="create_meeting",
|
||||
),
|
||||
path(
|
||||
"meetings/meeting-details/<slug:slug>/",
|
||||
views.ZoomMeetingDetailsView.as_view(),
|
||||
name="meeting_details",
|
||||
),
|
||||
path(
|
||||
"meetings/update-meeting/<slug:slug>/",
|
||||
views.ZoomMeetingUpdateView.as_view(),
|
||||
name="update_meeting",
|
||||
),
|
||||
path(
|
||||
"meetings/delete-meeting/<slug:slug>/",
|
||||
views.ZoomMeetingDeleteView,
|
||||
name="delete_meeting",
|
||||
),
|
||||
# JobPosting functional views URLs (keeping for compatibility)
|
||||
path('api/create/', views.create_job, name='create_job_api'),
|
||||
path('api/<slug:slug>/edit/', views.edit_job, name='edit_job_api'),
|
||||
|
||||
path("api/create/", views.create_job, name="create_job_api"),
|
||||
path("api/<slug:slug>/edit/", views.edit_job, name="edit_job_api"),
|
||||
# ERP Integration URLs
|
||||
path('integration/erp/', views_integration.ERPIntegrationView.as_view(), name='erp_integration'),
|
||||
path('integration/erp/create-job/', views_integration.erp_create_job_view, name='erp_create_job'),
|
||||
path('integration/erp/update-job/', views_integration.erp_update_job_view, name='erp_update_job'),
|
||||
path('integration/erp/health/', views_integration.erp_integration_health, name='erp_integration_health'),
|
||||
|
||||
path(
|
||||
"integration/erp/",
|
||||
views_integration.ERPIntegrationView.as_view(),
|
||||
name="erp_integration",
|
||||
),
|
||||
path(
|
||||
"integration/erp/create-job/",
|
||||
views_integration.erp_create_job_view,
|
||||
name="erp_create_job",
|
||||
),
|
||||
path(
|
||||
"integration/erp/update-job/",
|
||||
views_integration.erp_update_job_view,
|
||||
name="erp_update_job",
|
||||
),
|
||||
path(
|
||||
"integration/erp/health/",
|
||||
views_integration.erp_integration_health,
|
||||
name="erp_integration_health",
|
||||
),
|
||||
# Form Preview URLs
|
||||
# path('forms/', views.form_list, name='form_list'),
|
||||
|
||||
path('forms/builder/', views.form_builder, name='form_builder'),
|
||||
path('forms/builder/<slug:template_slug>/', views.form_builder, name='form_builder'),
|
||||
path('forms/', views.form_templates_list, name='form_templates_list'),
|
||||
path('forms/create-template/', views.create_form_template, name='create_form_template'),
|
||||
|
||||
path('jobs/<slug:slug>/edit_linkedin_post_content/',views.edit_linkedin_post_content,name='edit_linkedin_post_content'),
|
||||
path('jobs/<slug:slug>/candidate_screening_view/', views.candidate_screening_view, name='candidate_screening_view'),
|
||||
path('jobs/<slug:slug>/candidate_exam_view/', views.candidate_exam_view, name='candidate_exam_view'),
|
||||
path('jobs/<slug:slug>/candidate_interview_view/', views.candidate_interview_view, name='candidate_interview_view'),
|
||||
path('jobs/<slug:slug>/candidate_offer_view/', views_frontend.candidate_offer_view, name='candidate_offer_view'),
|
||||
path('jobs/<slug:slug>/candidate_hired_view/', views_frontend.candidate_hired_view, name='candidate_hired_view'),
|
||||
path('jobs/<slug:job_slug>/export/<str:stage>/csv/', views_frontend.export_candidates_csv, name='export_candidates_csv'),
|
||||
path('jobs/<slug:job_slug>/candidates/<slug:candidate_slug>/update_status/<str:stage_type>/<str:status>/', views_frontend.update_candidate_status, name='update_candidate_status'),
|
||||
|
||||
path("forms/builder/", views.form_builder, name="form_builder"),
|
||||
path(
|
||||
"forms/builder/<slug:template_slug>/", views.form_builder, name="form_builder"
|
||||
),
|
||||
path("forms/", views.form_templates_list, name="form_templates_list"),
|
||||
path(
|
||||
"forms/create-template/",
|
||||
views.create_form_template,
|
||||
name="create_form_template",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/edit_linkedin_post_content/",
|
||||
views.edit_linkedin_post_content,
|
||||
name="edit_linkedin_post_content",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/candidate_screening_view/",
|
||||
views.candidate_screening_view,
|
||||
name="candidate_screening_view",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/candidate_exam_view/",
|
||||
views.candidate_exam_view,
|
||||
name="candidate_exam_view",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/candidate_interview_view/",
|
||||
views.candidate_interview_view,
|
||||
name="candidate_interview_view",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/candidate_offer_view/",
|
||||
views_frontend.candidate_offer_view,
|
||||
name="candidate_offer_view",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/candidate_hired_view/",
|
||||
views_frontend.candidate_hired_view,
|
||||
name="candidate_hired_view",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:job_slug>/export/<str:stage>/csv/",
|
||||
views_frontend.export_candidates_csv,
|
||||
name="export_candidates_csv",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:job_slug>/candidates/<slug:candidate_slug>/update_status/<str:stage_type>/<str:status>/",
|
||||
views_frontend.update_candidate_status,
|
||||
name="update_candidate_status",
|
||||
),
|
||||
# Sync URLs
|
||||
path('jobs/<slug:job_slug>/sync-hired-candidates/', views_frontend.sync_hired_candidates, name='sync_hired_candidates'),
|
||||
path('sources/<int:source_id>/test-connection/', views_frontend.test_source_connection, name='test_source_connection'),
|
||||
|
||||
path('jobs/<slug:slug>/<int:candidate_id>/reschedule_meeting_for_candidate/<int:meeting_id>/', views.reschedule_meeting_for_candidate, name='reschedule_meeting_for_candidate'),
|
||||
|
||||
path('jobs/<slug:slug>/update_candidate_exam_status/', views.update_candidate_exam_status, name='update_candidate_exam_status'),
|
||||
path('jobs/<slug:slug>/bulk_update_candidate_exam_status/', views.bulk_update_candidate_exam_status, name='bulk_update_candidate_exam_status'),
|
||||
|
||||
path('htmx/<int:pk>/candidate_criteria_view/', views.candidate_criteria_view_htmx, name='candidate_criteria_view_htmx'),
|
||||
path('htmx/<slug:slug>/candidate_set_exam_date/', views.candidate_set_exam_date, name='candidate_set_exam_date'),
|
||||
|
||||
path('htmx/<slug:slug>/candidate_update_status/', views.candidate_update_status, name='candidate_update_status'),
|
||||
|
||||
path(
|
||||
"jobs/<slug:job_slug>/sync-hired-candidates/",
|
||||
views_frontend.sync_hired_candidates,
|
||||
name="sync_hired_candidates",
|
||||
),
|
||||
path(
|
||||
"sources/<int:source_id>/test-connection/",
|
||||
views_frontend.test_source_connection,
|
||||
name="test_source_connection",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/<int:candidate_id>/reschedule_meeting_for_candidate/<int:meeting_id>/",
|
||||
views.reschedule_meeting_for_candidate,
|
||||
name="reschedule_meeting_for_candidate",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/update_candidate_exam_status/",
|
||||
views.update_candidate_exam_status,
|
||||
name="update_candidate_exam_status",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/bulk_update_candidate_exam_status/",
|
||||
views.bulk_update_candidate_exam_status,
|
||||
name="bulk_update_candidate_exam_status",
|
||||
),
|
||||
path(
|
||||
"htmx/<int:pk>/candidate_criteria_view/",
|
||||
views.candidate_criteria_view_htmx,
|
||||
name="candidate_criteria_view_htmx",
|
||||
),
|
||||
path(
|
||||
"htmx/<slug:slug>/candidate_set_exam_date/",
|
||||
views.candidate_set_exam_date,
|
||||
name="candidate_set_exam_date",
|
||||
),
|
||||
path(
|
||||
"htmx/<slug:slug>/candidate_update_status/",
|
||||
views.candidate_update_status,
|
||||
name="candidate_update_status",
|
||||
),
|
||||
# path('forms/form/<slug:template_slug>/submit/', views.submit_form, name='submit_form'),
|
||||
# path('forms/form/<slug:template_slug>/', views.form_wizard_view, name='form_wizard'),
|
||||
path('forms/<int:template_id>/submissions/<slug:slug>/', views.form_submission_details, name='form_submission_details'),
|
||||
path('forms/template/<slug:slug>/submissions/', views.form_template_submissions_list, name='form_template_submissions_list'),
|
||||
path('forms/template/<int:template_id>/all-submissions/', views.form_template_all_submissions, name='form_template_all_submissions'),
|
||||
|
||||
path(
|
||||
"forms/<int:template_id>/submissions/<slug:slug>/",
|
||||
views.form_submission_details,
|
||||
name="form_submission_details",
|
||||
),
|
||||
path(
|
||||
"forms/template/<slug:slug>/submissions/",
|
||||
views.form_template_submissions_list,
|
||||
name="form_template_submissions_list",
|
||||
),
|
||||
path(
|
||||
"forms/template/<int:template_id>/all-submissions/",
|
||||
views.form_template_all_submissions,
|
||||
name="form_template_all_submissions",
|
||||
),
|
||||
# path('forms/<int:form_id>/', views.form_preview, name='form_preview'),
|
||||
# path('forms/<int:form_id>/submit/', views.form_submit, name='form_submit'),
|
||||
# path('forms/<int:form_id>/embed/', views.form_embed, name='form_embed'),
|
||||
@ -109,74 +275,188 @@ urlpatterns = [
|
||||
# path('api/templates/save/', views.save_form_template, name='save_form_template'),
|
||||
# path('api/templates/<slug:template_slug>/', views.load_form_template, name='load_form_template'),
|
||||
# path('api/templates/<slug:template_slug>/delete/', views.delete_form_template, name='delete_form_template'),
|
||||
|
||||
|
||||
path('jobs/<slug:slug>/calendar/', views.interview_calendar_view, name='interview_calendar'),
|
||||
path('jobs/<slug:slug>/calendar/interview/<int:interview_id>/', views.interview_detail_view, name='interview_detail'),
|
||||
|
||||
path(
|
||||
"jobs/<slug:slug>/calendar/",
|
||||
views.interview_calendar_view,
|
||||
name="interview_calendar",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/calendar/interview/<int:interview_id>/",
|
||||
views.interview_detail_view,
|
||||
name="interview_detail",
|
||||
),
|
||||
# Candidate Meeting Scheduling/Rescheduling URLs
|
||||
path('jobs/<slug:job_slug>/candidates/<int:candidate_pk>/schedule-meeting/', views.schedule_candidate_meeting, name='schedule_candidate_meeting'),
|
||||
path('api/jobs/<slug:job_slug>/candidates/<int:candidate_pk>/schedule-meeting/', views.api_schedule_candidate_meeting, name='api_schedule_candidate_meeting'),
|
||||
path('jobs/<slug:job_slug>/candidates/<int:candidate_pk>/reschedule-meeting/<int:interview_pk>/', views.reschedule_candidate_meeting, name='reschedule_candidate_meeting'),
|
||||
path('api/jobs/<slug:job_slug>/candidates/<int:candidate_pk>/reschedule-meeting/<int:interview_pk>/', views.api_reschedule_candidate_meeting, name='api_reschedule_candidate_meeting'),
|
||||
path(
|
||||
"jobs/<slug:job_slug>/candidates/<int:candidate_pk>/schedule-meeting/",
|
||||
views.schedule_candidate_meeting,
|
||||
name="schedule_candidate_meeting",
|
||||
),
|
||||
path(
|
||||
"api/jobs/<slug:job_slug>/candidates/<int:candidate_pk>/schedule-meeting/",
|
||||
views.api_schedule_candidate_meeting,
|
||||
name="api_schedule_candidate_meeting",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:job_slug>/candidates/<int:candidate_pk>/reschedule-meeting/<int:interview_pk>/",
|
||||
views.reschedule_candidate_meeting,
|
||||
name="reschedule_candidate_meeting",
|
||||
),
|
||||
path(
|
||||
"api/jobs/<slug:job_slug>/candidates/<int:candidate_pk>/reschedule-meeting/<int:interview_pk>/",
|
||||
views.api_reschedule_candidate_meeting,
|
||||
name="api_reschedule_candidate_meeting",
|
||||
),
|
||||
# New URL for simple page-based meeting scheduling
|
||||
path('jobs/<slug:slug>/candidates/<int:candidate_pk>/schedule-meeting-page/', views.schedule_meeting_for_candidate, name='schedule_meeting_for_candidate'),
|
||||
path('jobs/<slug:slug>/candidates/<int:candidate_pk>/delete_meeting_for_candidate/<int:meeting_id>/', views.delete_meeting_for_candidate, name='delete_meeting_for_candidate'),
|
||||
|
||||
|
||||
path(
|
||||
"jobs/<slug:slug>/candidates/<int:candidate_pk>/schedule-meeting-page/",
|
||||
views.schedule_meeting_for_candidate,
|
||||
name="schedule_meeting_for_candidate",
|
||||
),
|
||||
path(
|
||||
"jobs/<slug:slug>/candidates/<int:candidate_pk>/delete_meeting_for_candidate/<int:meeting_id>/",
|
||||
views.delete_meeting_for_candidate,
|
||||
name="delete_meeting_for_candidate",
|
||||
),
|
||||
# users urls
|
||||
path('user/<int:pk>',views.user_detail,name='user_detail'),
|
||||
path('user/user_profile_image_update/<int:pk>',views.user_profile_image_update,name='user_profile_image_update'),
|
||||
path('easy_logs/',views.easy_logs,name='easy_logs'),
|
||||
path('settings/',views.admin_settings,name='admin_settings'),
|
||||
path('staff/create',views.create_staff_user,name='create_staff_user'),
|
||||
path('set_staff_password/<int:pk>/',views.set_staff_password,name='set_staff_password'),
|
||||
path('account_toggle_status/<int:pk>',views.account_toggle_status,name='account_toggle_status'),
|
||||
|
||||
|
||||
|
||||
path("user/<int:pk>", views.user_detail, name="user_detail"),
|
||||
path(
|
||||
"user/user_profile_image_update/<int:pk>",
|
||||
views.user_profile_image_update,
|
||||
name="user_profile_image_update",
|
||||
),
|
||||
path("easy_logs/", views.easy_logs, name="easy_logs"),
|
||||
path("settings/", views.admin_settings, name="admin_settings"),
|
||||
path("staff/create", views.create_staff_user, name="create_staff_user"),
|
||||
path(
|
||||
"set_staff_password/<int:pk>/",
|
||||
views.set_staff_password,
|
||||
name="set_staff_password",
|
||||
),
|
||||
path(
|
||||
"account_toggle_status/<int:pk>",
|
||||
views.account_toggle_status,
|
||||
name="account_toggle_status",
|
||||
),
|
||||
# Source URLs
|
||||
path('sources/', views_source.SourceListView.as_view(), name='source_list'),
|
||||
path('sources/create/', views_source.SourceCreateView.as_view(), name='source_create'),
|
||||
path('sources/<int:pk>/', views_source.SourceDetailView.as_view(), name='source_detail'),
|
||||
path('sources/<int:pk>/update/', views_source.SourceUpdateView.as_view(), name='source_update'),
|
||||
path('sources/<int:pk>/delete/', views_source.SourceDeleteView.as_view(), name='source_delete'),
|
||||
path('sources/<int:pk>/generate-keys/', views_source.generate_api_keys_view, name='generate_api_keys'),
|
||||
path('sources/<int:pk>/toggle-status/', views_source.toggle_source_status_view, name='toggle_source_status'),
|
||||
path('sources/api/copy-to-clipboard/', views_source.copy_to_clipboard_view, name='copy_to_clipboard'),
|
||||
|
||||
|
||||
path("sources/", views_source.SourceListView.as_view(), name="source_list"),
|
||||
path(
|
||||
"sources/create/", views_source.SourceCreateView.as_view(), name="source_create"
|
||||
),
|
||||
path(
|
||||
"sources/<int:pk>/",
|
||||
views_source.SourceDetailView.as_view(),
|
||||
name="source_detail",
|
||||
),
|
||||
path(
|
||||
"sources/<int:pk>/update/",
|
||||
views_source.SourceUpdateView.as_view(),
|
||||
name="source_update",
|
||||
),
|
||||
path(
|
||||
"sources/<int:pk>/delete/",
|
||||
views_source.SourceDeleteView.as_view(),
|
||||
name="source_delete",
|
||||
),
|
||||
path(
|
||||
"sources/<int:pk>/generate-keys/",
|
||||
views_source.generate_api_keys_view,
|
||||
name="generate_api_keys",
|
||||
),
|
||||
path(
|
||||
"sources/<int:pk>/toggle-status/",
|
||||
views_source.toggle_source_status_view,
|
||||
name="toggle_source_status",
|
||||
),
|
||||
path(
|
||||
"sources/api/copy-to-clipboard/",
|
||||
views_source.copy_to_clipboard_view,
|
||||
name="copy_to_clipboard",
|
||||
),
|
||||
# Meeting Comments URLs
|
||||
path('meetings/<slug:slug>/comments/add/', views.add_meeting_comment, name='add_meeting_comment'),
|
||||
path('meetings/<slug:slug>/comments/<int:comment_id>/edit/', views.edit_meeting_comment, name='edit_meeting_comment'),
|
||||
|
||||
path('meetings/<slug:slug>/comments/<int:comment_id>/delete/', views.delete_meeting_comment, name='delete_meeting_comment'),
|
||||
|
||||
path('meetings/<slug:slug>/set_meeting_candidate/', views.set_meeting_candidate, name='set_meeting_candidate'),
|
||||
|
||||
path(
|
||||
"meetings/<slug:slug>/comments/add/",
|
||||
views.add_meeting_comment,
|
||||
name="add_meeting_comment",
|
||||
),
|
||||
path(
|
||||
"meetings/<slug:slug>/comments/<int:comment_id>/edit/",
|
||||
views.edit_meeting_comment,
|
||||
name="edit_meeting_comment",
|
||||
),
|
||||
path(
|
||||
"meetings/<slug:slug>/comments/<int:comment_id>/delete/",
|
||||
views.delete_meeting_comment,
|
||||
name="delete_meeting_comment",
|
||||
),
|
||||
path(
|
||||
"meetings/<slug:slug>/set_meeting_candidate/",
|
||||
views.set_meeting_candidate,
|
||||
name="set_meeting_candidate",
|
||||
),
|
||||
# Hiring Agency URLs
|
||||
path('agencies/', views.agency_list, name='agency_list'),
|
||||
path('agencies/create/', views.agency_create, name='agency_create'),
|
||||
path('agencies/<slug:slug>/', views.agency_detail, name='agency_detail'),
|
||||
path('agencies/<slug:slug>/update/', views.agency_update, name='agency_update'),
|
||||
path('agencies/<slug:slug>/delete/', views.agency_delete, name='agency_delete'),
|
||||
path('agencies/<slug:slug>/candidates/', views.agency_candidates, name='agency_candidates'),
|
||||
path("agencies/", views.agency_list, name="agency_list"),
|
||||
path("agencies/create/", views.agency_create, name="agency_create"),
|
||||
path("agencies/<slug:slug>/", views.agency_detail, name="agency_detail"),
|
||||
path("agencies/<slug:slug>/update/", views.agency_update, name="agency_update"),
|
||||
path("agencies/<slug:slug>/delete/", views.agency_delete, name="agency_delete"),
|
||||
path(
|
||||
"agencies/<slug:slug>/candidates/",
|
||||
views.agency_candidates,
|
||||
name="agency_candidates",
|
||||
),
|
||||
# path('agencies/<slug:slug>/send-message/', views.agency_detail_send_message, name='agency_detail_send_message'),
|
||||
|
||||
# Agency Assignment Management URLs
|
||||
path('agency-assignments/', views.agency_assignment_list, name='agency_assignment_list'),
|
||||
path('agency-assignments/create/', views.agency_assignment_create, name='agency_assignment_create'),
|
||||
path('agency-assignments/<slug:slug>/create/', views.agency_assignment_create, name='agency_assignment_create'),
|
||||
path('agency-assignments/<slug:slug>/', views.agency_assignment_detail, name='agency_assignment_detail'),
|
||||
path('agency-assignments/<slug:slug>/update/', views.agency_assignment_update, name='agency_assignment_update'),
|
||||
path('agency-assignments/<slug:slug>/extend-deadline/', views.agency_assignment_extend_deadline, name='agency_assignment_extend_deadline'),
|
||||
|
||||
path(
|
||||
"agency-assignments/",
|
||||
views.agency_assignment_list,
|
||||
name="agency_assignment_list",
|
||||
),
|
||||
path(
|
||||
"agency-assignments/create/",
|
||||
views.agency_assignment_create,
|
||||
name="agency_assignment_create",
|
||||
),
|
||||
path(
|
||||
"agency-assignments/<slug:slug>/create/",
|
||||
views.agency_assignment_create,
|
||||
name="agency_assignment_create",
|
||||
),
|
||||
path(
|
||||
"agency-assignments/<slug:slug>/",
|
||||
views.agency_assignment_detail,
|
||||
name="agency_assignment_detail",
|
||||
),
|
||||
path(
|
||||
"agency-assignments/<slug:slug>/update/",
|
||||
views.agency_assignment_update,
|
||||
name="agency_assignment_update",
|
||||
),
|
||||
path(
|
||||
"agency-assignments/<slug:slug>/extend-deadline/",
|
||||
views.agency_assignment_extend_deadline,
|
||||
name="agency_assignment_extend_deadline",
|
||||
),
|
||||
# Agency Access Link URLs
|
||||
path('agency-access-links/create/', views.agency_access_link_create, name='agency_access_link_create'),
|
||||
path('agency-access-links/<slug:slug>/', views.agency_access_link_detail, name='agency_access_link_detail'),
|
||||
path('agency-access-links/<slug:slug>/deactivate/', views.agency_access_link_deactivate, name='agency_access_link_deactivate'),
|
||||
path('agency-access-links/<slug:slug>/reactivate/', views.agency_access_link_reactivate, name='agency_access_link_reactivate'),
|
||||
|
||||
path(
|
||||
"agency-access-links/create/",
|
||||
views.agency_access_link_create,
|
||||
name="agency_access_link_create",
|
||||
),
|
||||
path(
|
||||
"agency-access-links/<slug:slug>/",
|
||||
views.agency_access_link_detail,
|
||||
name="agency_access_link_detail",
|
||||
),
|
||||
path(
|
||||
"agency-access-links/<slug:slug>/deactivate/",
|
||||
views.agency_access_link_deactivate,
|
||||
name="agency_access_link_deactivate",
|
||||
),
|
||||
path(
|
||||
"agency-access-links/<slug:slug>/reactivate/",
|
||||
views.agency_access_link_reactivate,
|
||||
name="agency_access_link_reactivate",
|
||||
),
|
||||
# Admin Message Center URLs (messaging functionality removed)
|
||||
# path('admin/messages/', views.admin_message_center, name='admin_message_center'),
|
||||
# path('admin/messages/compose/', views.admin_compose_message, name='admin_compose_message'),
|
||||
@ -184,35 +464,62 @@ urlpatterns = [
|
||||
# path('admin/messages/<int:message_id>/reply/', views.admin_message_reply, name='admin_message_reply'),
|
||||
# path('admin/messages/<int:message_id>/mark-read/', views.admin_mark_message_read, name='admin_mark_message_read'),
|
||||
# path('admin/messages/<int:message_id>/delete/', views.admin_delete_message, name='admin_delete_message'),
|
||||
|
||||
# Agency Portal URLs (for external agencies)
|
||||
path('portal/login/', views.agency_portal_login, name='agency_portal_login'),
|
||||
path('portal/dashboard/', views.agency_portal_dashboard, name='agency_portal_dashboard'),
|
||||
path('portal/assignment/<slug:slug>/', views.agency_portal_assignment_detail, name='agency_portal_assignment_detail'),
|
||||
path('portal/assignment/<slug:slug>/submit-candidate/', views.agency_portal_submit_candidate_page, name='agency_portal_submit_candidate_page'),
|
||||
path('portal/submit-candidate/', views.agency_portal_submit_candidate, name='agency_portal_submit_candidate'),
|
||||
path('portal/logout/', views.agency_portal_logout, name='agency_portal_logout'),
|
||||
|
||||
path("portal/login/", views.agency_portal_login, name="agency_portal_login"),
|
||||
path(
|
||||
"portal/dashboard/",
|
||||
views.agency_portal_dashboard,
|
||||
name="agency_portal_dashboard",
|
||||
),
|
||||
# Unified Portal URLs
|
||||
path("login/", views.portal_login, name="portal_login"),
|
||||
path(
|
||||
"candidate/dashboard/",
|
||||
views.candidate_portal_dashboard,
|
||||
name="candidate_portal_dashboard",
|
||||
),
|
||||
path(
|
||||
"portal/assignment/<slug:slug>/",
|
||||
views.agency_portal_assignment_detail,
|
||||
name="agency_portal_assignment_detail",
|
||||
),
|
||||
path(
|
||||
"portal/assignment/<slug:slug>/submit-candidate/",
|
||||
views.agency_portal_submit_candidate_page,
|
||||
name="agency_portal_submit_candidate_page",
|
||||
),
|
||||
path(
|
||||
"portal/submit-candidate/",
|
||||
views.agency_portal_submit_candidate,
|
||||
name="agency_portal_submit_candidate",
|
||||
),
|
||||
path("portal/logout/", views.portal_logout, name="portal_logout"),
|
||||
# Agency Portal Candidate Management URLs
|
||||
path('portal/candidates/<int:candidate_id>/edit/', views.agency_portal_edit_candidate, name='agency_portal_edit_candidate'),
|
||||
path('portal/candidates/<int:candidate_id>/delete/', views.agency_portal_delete_candidate, name='agency_portal_delete_candidate'),
|
||||
|
||||
path(
|
||||
"portal/candidates/<int:candidate_id>/edit/",
|
||||
views.agency_portal_edit_candidate,
|
||||
name="agency_portal_edit_candidate",
|
||||
),
|
||||
path(
|
||||
"portal/candidates/<int:candidate_id>/delete/",
|
||||
views.agency_portal_delete_candidate,
|
||||
name="agency_portal_delete_candidate",
|
||||
),
|
||||
# API URLs for messaging (removed)
|
||||
# path('api/agency/messages/<int:message_id>/', views.api_agency_message_detail, name='api_agency_message_detail'),
|
||||
# path('api/agency/messages/<int:message_id>/mark-read/', views.api_agency_mark_message_read, name='api_agency_mark_message_read'),
|
||||
|
||||
# API URLs for candidate management
|
||||
path('api/candidate/<int:candidate_id>/', views.api_candidate_detail, name='api_candidate_detail'),
|
||||
|
||||
path(
|
||||
"api/candidate/<int:candidate_id>/",
|
||||
views.api_candidate_detail,
|
||||
name="api_candidate_detail",
|
||||
),
|
||||
# # Admin Notification API
|
||||
# path('api/admin/notification-count/', views.api_notification_count, name='admin_notification_count'),
|
||||
|
||||
# # Agency Notification API
|
||||
# path('api/agency/notification-count/', views.api_notification_count, name='api_agency_notification_count'),
|
||||
|
||||
# # SSE Notification Stream
|
||||
# path('api/notifications/stream/', views.notification_stream, name='notification_stream'),
|
||||
|
||||
# # Notification URLs
|
||||
# path('notifications/', views.notification_list, name='notification_list'),
|
||||
# path('notifications/<int:notification_id>/', views.notification_detail, name='notification_detail'),
|
||||
@ -221,15 +528,36 @@ urlpatterns = [
|
||||
# path('notifications/<int:notification_id>/delete/', views.notification_delete, name='notification_delete'),
|
||||
# path('notifications/mark-all-read/', views.notification_mark_all_read, name='notification_mark_all_read'),
|
||||
# path('api/notification-count/', views.api_notification_count, name='api_notification_count'),
|
||||
|
||||
|
||||
#participants urls
|
||||
path('participants/', views_frontend.ParticipantsListView.as_view(), name='participants_list'),
|
||||
path('participants/create/', views_frontend.ParticipantsCreateView.as_view(), name='participants_create'),
|
||||
path('participants/<slug:slug>/', views_frontend.ParticipantsDetailView.as_view(), name='participants_detail'),
|
||||
path('participants/<slug:slug>/update/', views_frontend.ParticipantsUpdateView.as_view(), name='participants_update'),
|
||||
path('participants/<slug:slug>/delete/', views_frontend.ParticipantsDeleteView.as_view(), name='participants_delete'),
|
||||
|
||||
# participants urls
|
||||
path(
|
||||
"participants/",
|
||||
views_frontend.ParticipantsListView.as_view(),
|
||||
name="participants_list",
|
||||
),
|
||||
path(
|
||||
"participants/create/",
|
||||
views_frontend.ParticipantsCreateView.as_view(),
|
||||
name="participants_create",
|
||||
),
|
||||
path(
|
||||
"participants/<slug:slug>/",
|
||||
views_frontend.ParticipantsDetailView.as_view(),
|
||||
name="participants_detail",
|
||||
),
|
||||
path(
|
||||
"participants/<slug:slug>/update/",
|
||||
views_frontend.ParticipantsUpdateView.as_view(),
|
||||
name="participants_update",
|
||||
),
|
||||
path(
|
||||
"participants/<slug:slug>/delete/",
|
||||
views_frontend.ParticipantsDeleteView.as_view(),
|
||||
name="participants_delete",
|
||||
),
|
||||
# Email composition URLs
|
||||
path('jobs/<slug:job_slug>/candidates/<slug:candidate_slug>/compose-email/', views.compose_candidate_email, name='compose_candidate_email'),
|
||||
path(
|
||||
"jobs/<slug:job_slug>/candidates/<slug:candidate_slug>/compose-email/",
|
||||
views.compose_candidate_email,
|
||||
name="compose_candidate_email",
|
||||
),
|
||||
]
|
||||
|
||||
2605
recruitment/views.py
2605
recruitment/views.py
File diff suppressed because it is too large
Load Diff
@ -54,11 +54,18 @@
|
||||
<div style="background-color: #00636e;">
|
||||
<nav class="navbar navbar-expand-lg navbar-dark sticky-top">
|
||||
<div class="container-fluid" style="max-width: 1600px;">
|
||||
|
||||
{% if request.user.user_type == 'candidate' %}
|
||||
<a class="navbar-brand text-white" href="{% url 'candidate_portal_dashboard' %}" aria-label="Applicant Dashboard">
|
||||
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 40px; height: 40px;">
|
||||
<span class="ms-3 d-none d-md-inline fw-semibold">{% trans "Applicant Portal" %}</span>
|
||||
</a>
|
||||
{% elif request.user.user_type == 'agency' %}
|
||||
<a class="navbar-brand text-white" href="{% url 'agency_portal_dashboard' %}" aria-label="Agency Dashboard">
|
||||
<img src="{% static 'image/kaauh_green1.png' %}" alt="{% trans 'kaauh logo green bg' %}" style="width: 40px; height: 40px;">
|
||||
<span class="ms-3 d-none d-md-inline fw-semibold">{% trans "Agency Portal" %}</span>
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#agencyNavbar"
|
||||
aria-controls="agencyNavbar" aria-expanded="false" aria-label="{% trans 'Toggle navigation' %}">
|
||||
@ -97,7 +104,7 @@
|
||||
</li>
|
||||
|
||||
<li class="nav-item ms-3">
|
||||
<form method="post" action="{% url 'agency_portal_logout' %}" class="d-inline">
|
||||
<form method="post" action="{% url 'portal_logout' %}" class="d-inline">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-outline-light btn-sm">
|
||||
<i class="fas fa-sign-out-alt me-1"></i> {% trans "Logout" %}
|
||||
@ -134,7 +141,11 @@
|
||||
{% trans "All rights reserved." %}
|
||||
</p>
|
||||
<p class="mb-0 text-white-50">
|
||||
{% trans "Agency Portal" %}
|
||||
{% if request.user.user_type == 'candidate' %}
|
||||
{% trans "Candidate Portal" %}
|
||||
{% elif request.user.user_type == 'agency' %}
|
||||
{% trans "Agency Portal" %}
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends 'agency_base.html' %}
|
||||
{% extends 'portal_base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{% trans "Access Link Details" %} - ATS{% endblock %}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends 'agency_base.html' %}
|
||||
{% extends 'portal_base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{{ assignment.job.title }} - {{ assignment.agency.name }} - Agency Portal{% endblock %}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends 'agency_base.html' %}
|
||||
{% extends 'portal_base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{% trans "Agency Dashboard" %} - ATS{% endblock %}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends 'agency_base.html' %}
|
||||
{% extends 'portal_base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{% trans "Agency Portal Login" %} - ATS{% endblock %}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
{% extends 'agency_base.html' %}
|
||||
{% extends 'portal_base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{% trans "Submit Candidate" %} - {{ assignment.job.title }} - Agency Portal{% endblock %}
|
||||
|
||||
165
templates/recruitment/candidate_portal_dashboard.html
Normal file
165
templates/recruitment/candidate_portal_dashboard.html
Normal file
@ -0,0 +1,165 @@
|
||||
{% extends 'portal_base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{% trans "Candidate Dashboard" %} - ATS{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<!-- Dashboard Header -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6">
|
||||
<h4 class="mb-1">
|
||||
<i class="fas fa-user-tie me-2 text-primary"></i>
|
||||
{% trans "Welcome" %} {{ candidate.first_name }}
|
||||
</h4>
|
||||
<p class="text-muted mb-0">
|
||||
{% trans "Manage your applications and profile" %}
|
||||
</p>
|
||||
</div>
|
||||
{% comment %} <div class="col-md-6 text-md-end">
|
||||
<span class="badge bg-success fs-6">
|
||||
<i class="fas fa-circle me-1"></i>
|
||||
{% trans "Active" %}
|
||||
</span>
|
||||
</div> {% endcomment %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Stats -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-3">
|
||||
<i class="fas fa-briefcase fa-2x text-primary"></i>
|
||||
</div>
|
||||
<h5 class="card-title">{{ candidate.job.title|default:"No Job" }}</h5>
|
||||
<p class="text-muted small mb-0">
|
||||
{% trans "Applied Position" %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-3">
|
||||
<i class="fas fa-tasks fa-2x text-info"></i>
|
||||
</div>
|
||||
<h5 class="card-title">{{ candidate.stage|default:"Applied" }}</h5>
|
||||
<p class="text-muted small mb-0">
|
||||
{% trans "Current Stage" %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-body text-center">
|
||||
<div class="mb-3">
|
||||
<i class="fas fa-calendar fa-2x text-success"></i>
|
||||
</div>
|
||||
<h5 class="card-title">{{ candidate.created_at|date:"M d, Y" }}</h5>
|
||||
<p class="text-muted small mb-0">
|
||||
{% trans "Application Date" %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Profile Information -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0">
|
||||
<i class="fas fa-user me-2"></i>
|
||||
{% trans "Profile Information" %}
|
||||
</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">
|
||||
{% trans "Full Name" %}
|
||||
</label>
|
||||
<p class="form-control-plaintext">
|
||||
{{ candidate.first_name }} {{ candidate.last_name }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">
|
||||
{% trans "Email Address" %}
|
||||
</label>
|
||||
<p class="form-control-plaintext">
|
||||
{{ candidate.email }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">
|
||||
{% trans "Phone Number" %}
|
||||
</label>
|
||||
<p class="form-control-plaintext">
|
||||
{{ candidate.phone|default:"Not provided" }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label fw-bold">
|
||||
{% trans "Resume" %}
|
||||
</label>
|
||||
<p class="form-control-plaintext">
|
||||
{% if candidate.resume %}
|
||||
<a href="{{ candidate.resume.url }}" class="btn btn-sm btn-outline-primary" target="_blank">
|
||||
<i class="fas fa-download me-1"></i>
|
||||
{% trans "Download Resume" %}
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-muted">{% trans "No resume uploaded" %}</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="row text-center">
|
||||
<div class="col-md-4 mb-3">
|
||||
<button class="btn btn-outline-primary w-100">
|
||||
<i class="fas fa-edit me-2"></i>
|
||||
{% trans "Edit Profile" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<button class="btn btn-outline-success w-100">
|
||||
<i class="fas fa-file-upload me-2"></i>
|
||||
{% trans "Update Resume" %}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<button class="btn btn-outline-info w-100">
|
||||
<i class="fas fa-eye me-2"></i>
|
||||
{% trans "View Application" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
295
templates/recruitment/portal_login.html
Normal file
295
templates/recruitment/portal_login.html
Normal file
@ -0,0 +1,295 @@
|
||||
{% extends 'portal_base.html' %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block title %}{% trans "Portal Login" %} - ATS{% endblock %}
|
||||
|
||||
{% block customCSS %}
|
||||
<style>
|
||||
/* KAAT-S UI Variables */
|
||||
:root {
|
||||
--kaauh-teal: #00636e;
|
||||
--kaauh-teal-dark: #004a53;
|
||||
--kaauh-border: #eaeff3;
|
||||
--kaauh-primary-text: #343a40;
|
||||
--kaauh-success: #28a745;
|
||||
--kaauh-info: #17a2b8;
|
||||
--kaauh-danger: #dc3545;
|
||||
--kaauh-warning: #ffc107;
|
||||
}
|
||||
|
||||
body {
|
||||
background: linear-gradient(135deg, var(--kaauh-teal) 0%, var(--kaauh-teal-dark) 100%);
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
background: white;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
|
||||
border: none;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
margin: 0 1rem;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
background: linear-gradient(135deg, var(--kaauh-teal) 0%, var(--kaauh-teal-dark) 100%);
|
||||
color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 1rem 1rem 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-body {
|
||||
padding: 2.5rem;
|
||||
}
|
||||
|
||||
.form-control:focus {
|
||||
border-color: var(--kaauh-teal);
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 99, 110, 0.25);
|
||||
}
|
||||
|
||||
.btn-login {
|
||||
background: linear-gradient(135deg, var(--kaauh-teal) 0%, var(--kaauh-teal-dark) 100%);
|
||||
border: none;
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
padding: 0.75rem 2rem;
|
||||
border-radius: 0.5rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.btn-login:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(0, 99, 110, 0.3);
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
background-color: var(--kaauh-teal);
|
||||
border-color: var(--kaauh-teal);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.user-type-cards {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.user-type-card {
|
||||
border: 2px solid #e9ecef;
|
||||
border-radius: 0.5rem;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.user-type-card:hover {
|
||||
border-color: var(--kaauh-teal);
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.user-type-card.selected {
|
||||
border-color: var(--kaauh-teal);
|
||||
background-color: rgba(0, 99, 110, 0.1);
|
||||
}
|
||||
|
||||
.user-type-icon {
|
||||
font-size: 2rem;
|
||||
color: var(--kaauh-teal);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.alert {
|
||||
border-radius: 0.5rem;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="login-container">
|
||||
<div class="login-card">
|
||||
<!-- Login Header -->
|
||||
<div class="login-header">
|
||||
<div class="mb-3">
|
||||
<i class="fas fa-users fa-3x"></i>
|
||||
</div>
|
||||
<h3 class="mb-2">{% trans "Portal Login" %}</h3>
|
||||
<p class="mb-0 opacity-75">
|
||||
{% trans "Access your personalized dashboard" %}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Login Body -->
|
||||
<div class="login-body">
|
||||
<!-- Login Form -->
|
||||
<form method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
|
||||
|
||||
<!-- Email Field -->
|
||||
<div class="mb-3">
|
||||
<label for="{{ form.email.id_for_label }}" class="form-label fw-bold">
|
||||
<i class="fas fa-envelope me-2"></i>
|
||||
{% trans "Email Address" %}
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</span>
|
||||
{{ form.email }}
|
||||
</div>
|
||||
{% if form.email.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{% for error in form.email.errors %}{{ error }}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Password Field -->
|
||||
<div class="mb-4">
|
||||
<label for="{{ form.password.id_for_label }}" class="form-label fw-bold">
|
||||
<i class="fas fa-lock me-2"></i>
|
||||
{% trans "Password" %}
|
||||
</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="fas fa-key"></i>
|
||||
</span>
|
||||
{{ form.password }}
|
||||
</div>
|
||||
{% if form.password.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{% for error in form.password.errors %}{{ error }}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- User Type Selection -->
|
||||
<div class="mb-4">
|
||||
<label for="{{ form.user_type.id_for_label }}" class="form-label fw-bold">
|
||||
<i class="fas fa-user-tag me-2"></i>
|
||||
{% trans "Select User Type" %}
|
||||
</label>
|
||||
{{ form.user_type }}
|
||||
{% if form.user_type.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{% for error in form.user_type.errors %}{{ error }}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-login btn-lg">
|
||||
<i class="fas fa-sign-in-alt me-2"></i>
|
||||
{% trans "Login" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Help Links -->
|
||||
<div class="text-center mt-4">
|
||||
<small class="text-muted">
|
||||
{% trans "Need help?" %}
|
||||
<a href="#" class="text-decoration-none">
|
||||
{% trans "Contact Support" %}
|
||||
</a>
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block customJS %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Focus on user type field
|
||||
const userTypeField = document.getElementById('{{ form.user_type.id_for_label }}');
|
||||
if (userTypeField) {
|
||||
userTypeField.focus();
|
||||
}
|
||||
|
||||
// Form validation
|
||||
const form = document.querySelector('form');
|
||||
const emailField = document.getElementById('{{ form.email.id_for_label }}');
|
||||
const passwordField = document.getElementById('{{ form.password.id_for_label }}');
|
||||
|
||||
if (form) {
|
||||
form.addEventListener('submit', function(e) {
|
||||
const userType = userTypeField.value;
|
||||
const email = emailField.value.trim();
|
||||
const password = passwordField.value.trim();
|
||||
|
||||
if (!userType) {
|
||||
e.preventDefault();
|
||||
showError('{% trans "Please select a user type." %}');
|
||||
userTypeField.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!email) {
|
||||
e.preventDefault();
|
||||
showError('{% trans "Please enter your email address." %}');
|
||||
emailField.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!password) {
|
||||
e.preventDefault();
|
||||
showError('{% trans "Please enter your password." %}');
|
||||
passwordField.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
// Basic email validation
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email)) {
|
||||
e.preventDefault();
|
||||
showError('{% trans "Please enter a valid email address." %}');
|
||||
emailField.focus();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
// Remove existing alerts
|
||||
const existingAlerts = document.querySelectorAll('.alert-danger');
|
||||
existingAlerts.forEach(alert => alert.remove());
|
||||
|
||||
// Create new alert
|
||||
const alertDiv = document.createElement('div');
|
||||
alertDiv.className = 'alert alert-danger alert-dismissible fade show';
|
||||
alertDiv.innerHTML = `
|
||||
${message}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
`;
|
||||
|
||||
// Insert at the top of login body
|
||||
const loginBody = document.querySelector('.login-body');
|
||||
loginBody.insertBefore(alertDiv, loginBody.firstChild);
|
||||
|
||||
// Auto-dismiss after 5 seconds
|
||||
setTimeout(() => {
|
||||
if (alertDiv.parentNode) {
|
||||
alertDiv.remove();
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
Loading…
x
Reference in New Issue
Block a user