mirror of
https://github.com/thecyberlearn/quantum-ai-v2.git
synced 2026-08-18 15:13:00 +00:00
✨ Streamline user experience with cleaner UI and improved authentication flow
- Remove special styling for career navigator button, unify all Try Now buttons - Clean up authentication messaging system to prevent message confusion - Replace immediate success messages with direct redirections for smoother UX - Add session-based post-login messaging for better redirect handling - Improve login page with verification resend button and cleaner message display - Remove redundant payment success messages for free agents 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
bc662e6af0
commit
54d64f5927
@ -7,23 +7,7 @@
|
|||||||
<link rel="stylesheet" href="{% static 'css/agent-base.css' %}">
|
<link rel="stylesheet" href="{% static 'css/agent-base.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'css/marketplace.css' %}">
|
<link rel="stylesheet" href="{% static 'css/marketplace.css' %}">
|
||||||
<style>
|
<style>
|
||||||
/* Special styling for Career Navigator button */
|
/* All Try Now buttons now use consistent styling from marketplace.css */
|
||||||
.career-nav-btn {
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
|
||||||
color: white !important;
|
|
||||||
border: none !important;
|
|
||||||
font-weight: 600 !important;
|
|
||||||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3) !important;
|
|
||||||
transition: all 0.3s ease !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.career-nav-btn:hover {
|
|
||||||
transform: translateY(-2px) !important;
|
|
||||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4) !important;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Career nav card styling removed pulse animation for consistency */
|
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@ -92,12 +76,12 @@
|
|||||||
<div class="agent-footer">
|
<div class="agent-footer">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
{% if agent.slug == 'cybersec-career-navigator' %}
|
{% if agent.slug == 'cybersec-career-navigator' %}
|
||||||
<a href="{% url 'agents:career_navigator_access' %}" class="try-btn career-nav-btn">
|
<a href="{% url 'agents:career_navigator_access' %}" class="try-btn">
|
||||||
🎓 Try Now →
|
Try Now →
|
||||||
</a>
|
</a>
|
||||||
{% elif agent.slug == 'ai-brand-strategist' %}
|
{% elif agent.slug == 'ai-brand-strategist' %}
|
||||||
<a href="{% url 'agents:ai_brand_strategist_access' %}" class="try-btn">
|
<a href="{% url 'agents:ai_brand_strategist_access' %}" class="try-btn">
|
||||||
🧠 Try Now →
|
Try Now →
|
||||||
</a>
|
</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'agents:detail' agent.slug %}" class="try-btn">Try Now →</a>
|
<a href="{% url 'agents:detail' agent.slug %}" class="try-btn">Try Now →</a>
|
||||||
|
|||||||
@ -196,10 +196,12 @@ def execute_agent(request):
|
|||||||
def career_navigator_access(request):
|
def career_navigator_access(request):
|
||||||
"""Handle Try Now button click - charge wallet and redirect to form"""
|
"""Handle Try Now button click - charge wallet and redirect to form"""
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
# Clear any existing messages to prevent confusion
|
# Clear all existing messages before adding login message
|
||||||
storage = messages.get_messages(request)
|
storage = messages.get_messages(request)
|
||||||
storage.used = True
|
for _ in storage:
|
||||||
messages.error(request, 'Please login to access the Career Navigator.')
|
pass # Consume all messages
|
||||||
|
# Add login message to session for after login redirect
|
||||||
|
request.session['post_login_message'] = 'Please complete your login to access the Career Navigator.'
|
||||||
return redirect('authentication:login')
|
return redirect('authentication:login')
|
||||||
|
|
||||||
# Get the career navigator agent
|
# Get the career navigator agent
|
||||||
@ -240,21 +242,19 @@ def career_navigator_access(request):
|
|||||||
completed_at=timezone.now()
|
completed_at=timezone.now()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Success message and redirect to form
|
# Redirect directly to form - no message needed
|
||||||
if agent.price > 0:
|
|
||||||
messages.success(request, f'Welcome to your {agent.name} consultation.')
|
|
||||||
else:
|
|
||||||
messages.success(request, f'Welcome to your {agent.name} consultation.')
|
|
||||||
return redirect('agents:career_navigator')
|
return redirect('agents:career_navigator')
|
||||||
|
|
||||||
|
|
||||||
def career_navigator_view(request):
|
def career_navigator_view(request):
|
||||||
"""Display the career navigator form page"""
|
"""Display the career navigator form page"""
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
# Clear any existing messages to prevent confusion
|
# Clear all existing messages before adding login message
|
||||||
storage = messages.get_messages(request)
|
storage = messages.get_messages(request)
|
||||||
storage.used = True
|
for _ in storage:
|
||||||
messages.error(request, 'Please login to access the Career Navigator.')
|
pass # Consume all messages
|
||||||
|
# Add login message to session for after login redirect
|
||||||
|
request.session['post_login_message'] = 'Please complete your login to access the Career Navigator.'
|
||||||
return redirect('authentication:login')
|
return redirect('authentication:login')
|
||||||
|
|
||||||
# Get the career navigator agent
|
# Get the career navigator agent
|
||||||
@ -292,10 +292,12 @@ def career_navigator_view(request):
|
|||||||
def ai_brand_strategist_view(request):
|
def ai_brand_strategist_view(request):
|
||||||
"""Display the AI Brand Strategist form page"""
|
"""Display the AI Brand Strategist form page"""
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
# Clear any existing messages to prevent confusion
|
# Clear all existing messages before adding login message
|
||||||
storage = messages.get_messages(request)
|
storage = messages.get_messages(request)
|
||||||
storage.used = True
|
for _ in storage:
|
||||||
messages.error(request, 'Please login to access the AI Brand Strategist.')
|
pass # Consume all messages
|
||||||
|
# Add login message to session for after login redirect
|
||||||
|
request.session['post_login_message'] = 'Please complete your login to access the AI Brand Strategist.'
|
||||||
return redirect('authentication:login')
|
return redirect('authentication:login')
|
||||||
|
|
||||||
# Get the AI Brand Strategist agent
|
# Get the AI Brand Strategist agent
|
||||||
@ -333,10 +335,12 @@ def ai_brand_strategist_view(request):
|
|||||||
def ai_brand_strategist_access(request):
|
def ai_brand_strategist_access(request):
|
||||||
"""Handle Try Now button click - charge wallet and redirect to form"""
|
"""Handle Try Now button click - charge wallet and redirect to form"""
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
# Clear any existing messages to prevent confusion
|
# Clear all existing messages before adding login message
|
||||||
storage = messages.get_messages(request)
|
storage = messages.get_messages(request)
|
||||||
storage.used = True
|
for _ in storage:
|
||||||
messages.error(request, 'Please login to access the AI Brand Strategist.')
|
pass # Consume all messages
|
||||||
|
# Add login message to session for after login redirect
|
||||||
|
request.session['post_login_message'] = 'Please complete your login to access the AI Brand Strategist.'
|
||||||
return redirect('authentication:login')
|
return redirect('authentication:login')
|
||||||
|
|
||||||
# Get the AI Brand Strategist agent
|
# Get the AI Brand Strategist agent
|
||||||
@ -377,11 +381,7 @@ def ai_brand_strategist_access(request):
|
|||||||
completed_at=timezone.now()
|
completed_at=timezone.now()
|
||||||
)
|
)
|
||||||
|
|
||||||
# Success message and redirect to form
|
# Redirect directly to form - no message needed
|
||||||
if agent.price > 0:
|
|
||||||
messages.success(request, f'Welcome to your {agent.name} consultation.')
|
|
||||||
else:
|
|
||||||
messages.success(request, f'Welcome to your {agent.name} consultation.')
|
|
||||||
return redirect('agents:ai_brand_strategist')
|
return redirect('agents:ai_brand_strategist')
|
||||||
|
|
||||||
|
|
||||||
@ -1123,13 +1123,11 @@ def direct_access_handler(request, slug):
|
|||||||
description=f'Payment for {agent.name}',
|
description=f'Payment for {agent.name}',
|
||||||
agent_slug=agent.slug
|
agent_slug=agent.slug
|
||||||
)
|
)
|
||||||
messages.success(request, f'Payment of {agent.price} AED processed successfully.')
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
messages.error(request, 'Payment processing failed. Please try again.')
|
messages.error(request, 'Payment processing failed. Please try again.')
|
||||||
return redirect('agents:agent_detail', slug=slug)
|
return redirect('agents:agent_detail', slug=slug)
|
||||||
|
|
||||||
# Grant access - redirect to display page
|
# Grant access - redirect directly to display page
|
||||||
messages.success(request, f'Access granted to {agent.name}. Redirecting to consultation form...')
|
|
||||||
return redirect('agents:direct_access_display', slug=slug)
|
return redirect('agents:direct_access_display', slug=slug)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -89,10 +89,17 @@ def handle_ratelimited(request, exception):
|
|||||||
@ratelimit(key='ip', rate='5/m', method=UNSAFE, block=False)
|
@ratelimit(key='ip', rate='5/m', method=UNSAFE, block=False)
|
||||||
def login_view(request):
|
def login_view(request):
|
||||||
"""User login view with rate limiting (5 attempts per minute per IP)"""
|
"""User login view with rate limiting (5 attempts per minute per IP)"""
|
||||||
|
# Handle post-login session messages
|
||||||
|
if 'post_login_message' in request.session:
|
||||||
|
messages.info(request, request.session.pop('post_login_message'))
|
||||||
|
|
||||||
# Check if rate limited
|
# Check if rate limited
|
||||||
if getattr(request, 'limited', False):
|
if getattr(request, 'limited', False):
|
||||||
logger.warning(f"Login rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
|
# Use session to avoid repeated rate limit messages
|
||||||
messages.error(request, 'Too many login attempts. Please try again in a few minutes.')
|
if not request.session.get('rate_limit_shown'):
|
||||||
|
logger.warning(f"Login rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
|
||||||
|
messages.error(request, 'Too many login attempts. Please wait before trying again.')
|
||||||
|
request.session['rate_limit_shown'] = True
|
||||||
return render(request, 'authentication/login.html')
|
return render(request, 'authentication/login.html')
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@ -103,12 +110,14 @@ def login_view(request):
|
|||||||
if user is not None:
|
if user is not None:
|
||||||
# Check if email is verified (only if email verification is required)
|
# Check if email is verified (only if email verification is required)
|
||||||
if settings.REQUIRE_EMAIL_VERIFICATION and not user.email_verified:
|
if settings.REQUIRE_EMAIL_VERIFICATION and not user.email_verified:
|
||||||
from django.urls import reverse
|
messages.warning(request, 'Please verify your email address before logging in. Check your inbox for the verification link.')
|
||||||
resend_url = reverse('authentication:resend_verification')
|
# Store resend URL in context for template
|
||||||
messages.warning(request, f'Please verify your email address before logging in. Check your inbox for the verification link, or <a href="{resend_url}">resend verification email</a>.')
|
context = {'show_resend_verification': True}
|
||||||
return render(request, 'authentication/login.html')
|
return render(request, 'authentication/login.html', context)
|
||||||
|
|
||||||
login(request, user)
|
login(request, user)
|
||||||
|
# Clear rate limit flag on successful login
|
||||||
|
request.session.pop('rate_limit_shown', None)
|
||||||
# Redirect to 'next' parameter if provided, otherwise homepage
|
# Redirect to 'next' parameter if provided, otherwise homepage
|
||||||
next_url = request.GET.get('next') or request.POST.get('next')
|
next_url = request.GET.get('next') or request.POST.get('next')
|
||||||
if next_url:
|
if next_url:
|
||||||
@ -116,6 +125,8 @@ def login_view(request):
|
|||||||
return redirect('core:homepage')
|
return redirect('core:homepage')
|
||||||
else:
|
else:
|
||||||
messages.error(request, 'Invalid email or password')
|
messages.error(request, 'Invalid email or password')
|
||||||
|
# Clear rate limit flag on any POST attempt (failed login)
|
||||||
|
request.session.pop('rate_limit_shown', None)
|
||||||
|
|
||||||
return render(request, 'authentication/login.html')
|
return render(request, 'authentication/login.html')
|
||||||
|
|
||||||
|
|||||||
@ -104,6 +104,37 @@
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.verification-actions {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: var(--spacing-lg);
|
||||||
|
padding: var(--spacing-md);
|
||||||
|
background: var(--surface-variant);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
border: 1px solid var(--outline);
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-text {
|
||||||
|
margin: 0 0 var(--spacing-sm) 0;
|
||||||
|
color: var(--on-surface-variant);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid var(--primary);
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline:hover {
|
||||||
|
background: var(--primary);
|
||||||
|
color: var(--on-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
padding: var(--spacing-xs) var(--spacing-md);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Responsive */
|
/* Responsive */
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
.login-page {
|
.login-page {
|
||||||
@ -132,11 +163,18 @@
|
|||||||
{% if messages %}
|
{% if messages %}
|
||||||
<div class="messages">
|
<div class="messages">
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<div class="message {{ message.tags }}">{{ message|safe }}</div>
|
<div class="message {{ message.tags }}">{{ message }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if show_resend_verification %}
|
||||||
|
<div class="verification-actions">
|
||||||
|
<p class="verification-text">Need to resend verification email?</p>
|
||||||
|
<a href="{% url 'authentication:resend_verification' %}" class="btn btn-outline btn-sm">Resend Verification</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<form method="post" class="login-form">
|
<form method="post" class="login-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if request.GET.next %}
|
{% if request.GET.next %}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user