mirror of
https://github.com/thecyberlearn/quantum-ai-v2.git
synced 2026-08-18 08:52:59 +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/marketplace.css' %}">
|
||||
<style>
|
||||
/* Special styling for Career Navigator button */
|
||||
.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 */
|
||||
/* All Try Now buttons now use consistent styling from marketplace.css */
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@ -92,12 +76,12 @@
|
||||
<div class="agent-footer">
|
||||
{% if user.is_authenticated %}
|
||||
{% if agent.slug == 'cybersec-career-navigator' %}
|
||||
<a href="{% url 'agents:career_navigator_access' %}" class="try-btn career-nav-btn">
|
||||
🎓 Try Now →
|
||||
<a href="{% url 'agents:career_navigator_access' %}" class="try-btn">
|
||||
Try Now →
|
||||
</a>
|
||||
{% elif agent.slug == 'ai-brand-strategist' %}
|
||||
<a href="{% url 'agents:ai_brand_strategist_access' %}" class="try-btn">
|
||||
🧠 Try Now →
|
||||
Try Now →
|
||||
</a>
|
||||
{% else %}
|
||||
<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):
|
||||
"""Handle Try Now button click - charge wallet and redirect to form"""
|
||||
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.used = True
|
||||
messages.error(request, 'Please login to access the Career Navigator.')
|
||||
for _ in storage:
|
||||
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')
|
||||
|
||||
# Get the career navigator agent
|
||||
@ -240,21 +242,19 @@ def career_navigator_access(request):
|
||||
completed_at=timezone.now()
|
||||
)
|
||||
|
||||
# Success message and redirect to form
|
||||
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.')
|
||||
# Redirect directly to form - no message needed
|
||||
return redirect('agents:career_navigator')
|
||||
|
||||
|
||||
def career_navigator_view(request):
|
||||
"""Display the career navigator form page"""
|
||||
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.used = True
|
||||
messages.error(request, 'Please login to access the Career Navigator.')
|
||||
for _ in storage:
|
||||
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')
|
||||
|
||||
# Get the career navigator agent
|
||||
@ -292,10 +292,12 @@ def career_navigator_view(request):
|
||||
def ai_brand_strategist_view(request):
|
||||
"""Display the AI Brand Strategist form page"""
|
||||
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.used = True
|
||||
messages.error(request, 'Please login to access the AI Brand Strategist.')
|
||||
for _ in storage:
|
||||
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')
|
||||
|
||||
# Get the AI Brand Strategist agent
|
||||
@ -333,10 +335,12 @@ def ai_brand_strategist_view(request):
|
||||
def ai_brand_strategist_access(request):
|
||||
"""Handle Try Now button click - charge wallet and redirect to form"""
|
||||
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.used = True
|
||||
messages.error(request, 'Please login to access the AI Brand Strategist.')
|
||||
for _ in storage:
|
||||
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')
|
||||
|
||||
# Get the AI Brand Strategist agent
|
||||
@ -377,11 +381,7 @@ def ai_brand_strategist_access(request):
|
||||
completed_at=timezone.now()
|
||||
)
|
||||
|
||||
# Success message and redirect to form
|
||||
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.')
|
||||
# Redirect directly to form - no message needed
|
||||
return redirect('agents:ai_brand_strategist')
|
||||
|
||||
|
||||
@ -1123,13 +1123,11 @@ def direct_access_handler(request, slug):
|
||||
description=f'Payment for {agent.name}',
|
||||
agent_slug=agent.slug
|
||||
)
|
||||
messages.success(request, f'Payment of {agent.price} AED processed successfully.')
|
||||
except Exception as e:
|
||||
messages.error(request, 'Payment processing failed. Please try again.')
|
||||
return redirect('agents:agent_detail', slug=slug)
|
||||
|
||||
# Grant access - redirect to display page
|
||||
messages.success(request, f'Access granted to {agent.name}. Redirecting to consultation form...')
|
||||
# Grant access - redirect directly to display page
|
||||
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)
|
||||
def login_view(request):
|
||||
"""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
|
||||
if getattr(request, 'limited', False):
|
||||
logger.warning(f"Login rate limit exceeded for IP {request.META.get('REMOTE_ADDR')}")
|
||||
messages.error(request, 'Too many login attempts. Please try again in a few minutes.')
|
||||
# Use session to avoid repeated rate limit messages
|
||||
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')
|
||||
|
||||
if request.method == 'POST':
|
||||
@ -103,12 +110,14 @@ def login_view(request):
|
||||
if user is not None:
|
||||
# Check if email is verified (only if email verification is required)
|
||||
if settings.REQUIRE_EMAIL_VERIFICATION and not user.email_verified:
|
||||
from django.urls import reverse
|
||||
resend_url = reverse('authentication:resend_verification')
|
||||
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>.')
|
||||
return render(request, 'authentication/login.html')
|
||||
messages.warning(request, 'Please verify your email address before logging in. Check your inbox for the verification link.')
|
||||
# Store resend URL in context for template
|
||||
context = {'show_resend_verification': True}
|
||||
return render(request, 'authentication/login.html', context)
|
||||
|
||||
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
|
||||
next_url = request.GET.get('next') or request.POST.get('next')
|
||||
if next_url:
|
||||
@ -116,6 +125,8 @@ def login_view(request):
|
||||
return redirect('core:homepage')
|
||||
else:
|
||||
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')
|
||||
|
||||
|
||||
@ -104,6 +104,37 @@
|
||||
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 */
|
||||
@media (max-width: 480px) {
|
||||
.login-page {
|
||||
@ -132,11 +163,18 @@
|
||||
{% if messages %}
|
||||
<div class="messages">
|
||||
{% for message in messages %}
|
||||
<div class="message {{ message.tags }}">{{ message|safe }}</div>
|
||||
<div class="message {{ message.tags }}">{{ message }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% 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">
|
||||
{% csrf_token %}
|
||||
{% if request.GET.next %}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user