245 lines
11 KiB
HTML
245 lines
11 KiB
HTML
{% extends 'portal_base.html' %}
|
|
{% load static i18n %}
|
|
|
|
{% block title %}{% trans "Agency Portal Login" %} - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<body class="bg-gradient-to-br from-temple-red to-[#7a1a29] min-h-screen">
|
|
<div class="min-h-screen flex items-center justify-center p-6">
|
|
<div class="bg-white rounded-2xl shadow-2xl border-none max-w-2xl w-full">
|
|
|
|
<!-- Login Header -->
|
|
<div class="bg-gradient-to-br from-temple-red to-[#7a1a29] text-white p-8 rounded-t-2xl text-center">
|
|
<div class="mb-4 inline-flex items-center justify-center w-20 h-20 rounded-full bg-white/20 backdrop-blur-sm">
|
|
<i data-lucide="building" class="w-10 h-10"></i>
|
|
</div>
|
|
<h2 class="text-2xl md:text-3xl font-bold mb-2">{% trans "Agency Portal" %}</h2>
|
|
<p class="text-white/80 text-sm md:text-base">
|
|
{% trans "Submit candidates for job assignments" %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Login Body -->
|
|
<div class="p-8 md:p-10">
|
|
|
|
<!-- Login Form -->
|
|
<form method="post" novalidate id="login-form" class="space-y-6">
|
|
{% csrf_token %}
|
|
|
|
<!-- Access Token Field -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.token.id_for_label }}" class="block text-sm font-bold text-gray-700">
|
|
<i data-lucide="key" class="w-4 h-4 inline mr-2"></i>
|
|
{% trans "Access Token" %}
|
|
</label>
|
|
<div class="relative">
|
|
<span class="absolute left-4 top-1/2 -translate-y-1/2 text-temple-red">
|
|
<i data-lucide="lock" class="w-5 h-5"></i>
|
|
</span>
|
|
<input type="text"
|
|
name="token"
|
|
id="{{ form.token.id_for_label }}"
|
|
class="w-full pl-12 pr-4 py-3.5 border border-gray-300 rounded-xl text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition"
|
|
placeholder="{% trans 'Enter your access token' %}"
|
|
required>
|
|
</div>
|
|
{% if form.token.errors %}
|
|
<div class="text-red-600 text-sm mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-4 h-4"></i>
|
|
{% for error in form.token.errors %}{{ error }}{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
<p class="text-xs text-gray-500">
|
|
{% trans "Enter the access token provided by the hiring organization" %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Password Field -->
|
|
<div class="space-y-2">
|
|
<label for="{{ form.password.id_for_label }}" class="block text-sm font-bold text-gray-700">
|
|
<i data-lucide="shield-check" class="w-4 h-4 inline mr-2"></i>
|
|
{% trans "Password" %}
|
|
</label>
|
|
<div class="relative">
|
|
<span class="absolute left-4 top-1/2 -translate-y-1/2 text-temple-red">
|
|
<i data-lucide="key" class="w-5 h-5"></i>
|
|
</span>
|
|
<input type="password"
|
|
name="password"
|
|
id="{{ form.password.id_for_label }}"
|
|
class="w-full pl-12 pr-12 py-3.5 border border-gray-300 rounded-xl text-sm focus:ring-2 focus:ring-temple-red/20 focus:border-temple-red outline-none transition"
|
|
placeholder="{% trans 'Enter your password' %}"
|
|
required>
|
|
<button type="button"
|
|
onclick="togglePassword()"
|
|
id="password-toggle"
|
|
class="absolute right-4 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 transition">
|
|
<i data-lucide="eye" class="w-5 h-5"></i>
|
|
</button>
|
|
</div>
|
|
{% if form.password.errors %}
|
|
<div class="text-red-600 text-sm mt-1 flex items-center gap-1">
|
|
<i data-lucide="alert-circle" class="w-4 h-4"></i>
|
|
{% for error in form.password.errors %}{{ error }}{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
<p class="text-xs text-gray-500">
|
|
{% trans "Enter the password for this access token" %}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Submit Button -->
|
|
<button type="submit"
|
|
class="w-full bg-gradient-to-r from-temple-red to-[#7a1a29] hover:from-[#8a1d2d] hover:to-[#6a1520] text-white font-bold py-4 rounded-xl text-sm transition shadow-lg hover:shadow-xl transform hover:-translate-y-0.5 flex items-center justify-center gap-2">
|
|
<i data-lucide="log-in" class="w-5 h-5"></i>
|
|
{% trans "Access Portal" %}
|
|
</button>
|
|
</form>
|
|
|
|
<!-- Information Section -->
|
|
<div class="mt-8 bg-temple-cream rounded-xl p-6 border border-gray-200">
|
|
<h5 class="text-temple-dark font-bold mb-4 flex items-center gap-2">
|
|
<i data-lucide="help-circle" class="w-5 h-5 text-temple-red"></i>
|
|
{% trans "Need Help?" %}
|
|
</h5>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-center">
|
|
<div>
|
|
<div class="w-16 h-16 mx-auto mb-3 rounded-full bg-gradient-to-br from-temple-red to-[#7a1a29] flex items-center justify-center text-white">
|
|
<i data-lucide="mail" class="w-8 h-8"></i>
|
|
</div>
|
|
<h6 class="font-bold text-gray-900 mb-1">{% trans "Contact Support" %}</h6>
|
|
<p class="text-sm text-gray-500">
|
|
{% trans "Reach out to your hiring contact" %}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<div class="w-16 h-16 mx-auto mb-3 rounded-full bg-gradient-to-br from-temple-red to-[#7a1a29] flex items-center justify-center text-white">
|
|
<i data-lucide="book-open" class="w-8 h-8"></i>
|
|
</div>
|
|
<h6 class="font-bold text-gray-900 mb-1">{% trans "Documentation" %}</h6>
|
|
<p class="text-sm text-gray-500">
|
|
{% trans "View user guides and tutorials" %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Security Notice -->
|
|
<div class="mt-6 bg-blue-50 border border-blue-200 rounded-xl p-5">
|
|
<h6 class="text-blue-800 font-bold mb-3 flex items-center gap-2">
|
|
<i data-lucide="shield-check" class="w-4 h-4"></i>
|
|
{% trans "Security Notice" %}
|
|
</h6>
|
|
<p class="text-blue-700 text-sm mb-3">
|
|
{% trans "This portal is for authorized agency partners only. Access is monitored and logged." %}
|
|
</p>
|
|
<hr class="border-blue-200 mb-3">
|
|
<p class="text-blue-700 text-sm mb-0">
|
|
{% trans "If you believe you've received this link in error, please contact the hiring organization immediately." %}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast Notification Container -->
|
|
<div id="toast-container"></div>
|
|
</body>
|
|
{% endblock %}
|
|
|
|
{% block customJS %}
|
|
<script>
|
|
lucide.createIcons();
|
|
|
|
// Auto-format access token (remove spaces and convert to uppercase)
|
|
const accessTokenInput = document.getElementById('{{ form.token.id_for_label }}');
|
|
if (accessTokenInput) {
|
|
accessTokenInput.addEventListener('input', function() {
|
|
// Remove spaces and convert to uppercase
|
|
this.value = this.value.replace(/\s+/g, '').toUpperCase();
|
|
});
|
|
|
|
// Focus on load
|
|
accessTokenInput.focus();
|
|
}
|
|
|
|
// Toggle password visibility
|
|
function togglePassword() {
|
|
const passwordField = document.getElementById('{{ form.password.id_for_label }}');
|
|
const toggleBtn = document.getElementById('password-toggle');
|
|
|
|
if (passwordField && toggleBtn) {
|
|
const type = passwordField.getAttribute('type') === 'password' ? 'text' : 'password';
|
|
passwordField.setAttribute('type', type);
|
|
|
|
const icon = type === 'password' ? 'eye' : 'eye-off';
|
|
toggleBtn.innerHTML = `<i data-lucide="${icon}" class="w-5 h-5"></i>`;
|
|
lucide.createIcons();
|
|
}
|
|
}
|
|
|
|
// Form validation
|
|
const form = document.getElementById('login-form');
|
|
if (form) {
|
|
form.addEventListener('submit', function(e) {
|
|
const accessToken = accessTokenInput.value.trim();
|
|
const passwordField = document.getElementById('{{ form.password.id_for_label }}');
|
|
const password = passwordField ? passwordField.value.trim() : '';
|
|
|
|
if (!accessToken) {
|
|
e.preventDefault();
|
|
showError('{% trans "Please enter your access token." %}');
|
|
accessTokenInput.focus();
|
|
return;
|
|
}
|
|
|
|
if (!password) {
|
|
e.preventDefault();
|
|
showError('{% trans "Please enter your password." %}');
|
|
passwordField.focus();
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
|
|
function showError(message) {
|
|
// Remove existing toasts
|
|
const container = document.getElementById('toast-container');
|
|
container.innerHTML = '';
|
|
|
|
// Create new toast
|
|
const toast = document.createElement('div');
|
|
toast.className = 'fixed top-4 right-4 bg-red-600 text-white px-6 py-4 rounded-xl shadow-lg z-50 flex items-center gap-3 animate-pulse';
|
|
toast.innerHTML = `
|
|
<i data-lucide="alert-circle" class="w-5 h-5"></i>
|
|
<span class="flex-1">${message}</span>
|
|
<button onclick="this.parentElement.remove()" class="hover:bg-red-700 p-1 rounded-lg transition">
|
|
<i data-lucide="x" class="w-4 h-4"></i>
|
|
</button>
|
|
`;
|
|
|
|
container.appendChild(toast);
|
|
lucide.createIcons();
|
|
|
|
// Auto-dismiss after 5 seconds
|
|
setTimeout(() => {
|
|
if (toast.parentNode) {
|
|
toast.classList.add('animate-fade-out');
|
|
setTimeout(() => toast.remove(), 300);
|
|
}
|
|
}, 5000);
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
@keyframes fade-out {
|
|
from { opacity: 1; transform: translateY(0); }
|
|
to { opacity: 0; transform: translateY(-20px); }
|
|
}
|
|
|
|
.animate-fade-out {
|
|
animation: fade-out 0.3s ease-out forwards;
|
|
}
|
|
</style>
|
|
{% endblock %} |