mirror of
https://github.com/thecyberlearn/quantum-ai-v3.git
synced 2026-08-18 13:52:57 +00:00
🔧 Fix payment message persistence and add expired session popup
- Fix wallet payment messages showing on login page by clearing messages before redirects - Remove misleading "Payment processed\!" message for free agents - Implement minimalist expired session popup for 5 Whys agent - Add auto-detection of expired sessions on page load with payment confirmation - Streamline authentication flow messages for better UX - Remove logout confirmation message to prevent persistence 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b9c6264b0e
commit
ae303c2621
@ -424,6 +424,78 @@
|
||||
padding: 20px !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* Expired Session Modal */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
background: var(--surface);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 30px;
|
||||
max-width: 400px;
|
||||
width: 90%;
|
||||
text-align: center;
|
||||
box-shadow: var(--shadow-xl);
|
||||
border: 1px solid var(--outline);
|
||||
}
|
||||
|
||||
.modal-content h3 {
|
||||
margin: 0 0 15px 0;
|
||||
color: var(--on-surface);
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.modal-content p {
|
||||
margin: 0 0 25px 0;
|
||||
color: var(--on-surface-variant);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.modal-actions .btn {
|
||||
padding: 10px 20px;
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.modal-actions .btn-primary {
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal-actions .btn-primary:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.modal-actions .btn-secondary {
|
||||
background: var(--surface-variant);
|
||||
color: var(--on-surface);
|
||||
border: 1px solid var(--outline);
|
||||
}
|
||||
|
||||
.modal-actions .btn-secondary:hover {
|
||||
background: var(--background);
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@ -584,6 +656,22 @@ document.body.setAttribute('data-session-expires', '{{ chat_session.expires_at.i
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Expired Session Modal -->
|
||||
<div id="expiredSessionModal" class="modal-overlay" style="display: none;">
|
||||
<div class="modal-content">
|
||||
<h3>Session Expired</h3>
|
||||
<p>Start new chat session?<br>This will deduct {{ agent.price }} AED from your wallet.</p>
|
||||
<div class="modal-actions">
|
||||
<button onclick="startNewSessionFromExpired()" class="btn btn-primary">
|
||||
Continue ({{ agent.price }} AED)
|
||||
</button>
|
||||
<button onclick="cancelNewSession()" class="btn btn-secondary">
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="agent-sidebar">
|
||||
<!-- How It Works Widget -->
|
||||
@ -795,6 +883,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
if (sessionId) {
|
||||
// Clear old session start time cache
|
||||
localStorage.removeItem(`session_start_${sessionId}`);
|
||||
|
||||
// Check if session is expired on page load
|
||||
checkExpiredSessionOnLoad(sessionId);
|
||||
}
|
||||
|
||||
// Initialize session indicators updates
|
||||
@ -1035,5 +1126,91 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
console.log('Insufficient balance for chat session');
|
||||
}
|
||||
});
|
||||
|
||||
// Expired Session Detection and Modal Functions
|
||||
function checkExpiredSessionOnLoad(sessionId) {
|
||||
// Only check if user is authenticated
|
||||
const isAuthenticated = document.body.getAttribute('data-user-authenticated') === 'true';
|
||||
if (!isAuthenticated) return;
|
||||
|
||||
// Check session status
|
||||
fetch(`/agents/api/chat/session/${sessionId}/status/`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-CSRFToken': getCSRFToken(),
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.is_expired) {
|
||||
// Session is expired, show modal
|
||||
showExpiredSessionModal();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error checking session status:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function showExpiredSessionModal() {
|
||||
const modal = document.getElementById('expiredSessionModal');
|
||||
if (modal) {
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
}
|
||||
|
||||
function hideExpiredSessionModal() {
|
||||
const modal = document.getElementById('expiredSessionModal');
|
||||
if (modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
async function startNewSessionFromExpired() {
|
||||
const agentSlug = document.body.getAttribute('data-agent-slug');
|
||||
const userBalance = parseFloat(document.body.getAttribute('data-user-balance') || '0');
|
||||
const agentPrice = parseFloat(document.body.getAttribute('data-agent-price') || '0');
|
||||
|
||||
// Check balance first
|
||||
if (userBalance < agentPrice) {
|
||||
alert('Insufficient balance. Please top up your wallet.');
|
||||
window.location.href = '/wallet/';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Start new session (this will deduct payment automatically)
|
||||
const response = await fetch('/agents/api/chat/start/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': getCSRFToken()
|
||||
},
|
||||
body: JSON.stringify({
|
||||
agent_slug: agentSlug
|
||||
})
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (response.ok) {
|
||||
// Success - hide modal and reload page to show new session
|
||||
hideExpiredSessionModal();
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert(data.error || 'Failed to start new session');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error starting new session:', error);
|
||||
alert('Failed to start new session');
|
||||
}
|
||||
}
|
||||
|
||||
function cancelNewSession() {
|
||||
hideExpiredSessionModal();
|
||||
// Optionally redirect to marketplace or disable chat interface
|
||||
window.location.href = '/agents/';
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@ -241,7 +241,10 @@ def career_navigator_access(request):
|
||||
)
|
||||
|
||||
# Success message and redirect to form
|
||||
messages.success(request, f'✅ Payment processed! Welcome to your {agent.name} consultation.')
|
||||
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')
|
||||
|
||||
|
||||
|
||||
@ -30,7 +30,7 @@ def validate_password_strength(password):
|
||||
is_common = password.lower() in common_passwords
|
||||
|
||||
if not (has_length and has_lower and has_upper and has_digit and has_special) or is_common:
|
||||
return ["Password must be at least 8 characters with uppercase, lowercase, number, and special character"]
|
||||
return ["Password must have 8+ characters, uppercase, lowercase, number, and special character"]
|
||||
|
||||
return []
|
||||
|
||||
@ -162,9 +162,9 @@ def register_view(request):
|
||||
# Don't automatically login - require email verification first
|
||||
# Send verification email
|
||||
if send_verification_email(user):
|
||||
messages.success(request, 'Account created successfully! Please check your email to verify your account.')
|
||||
messages.success(request, 'Account created. Check your email to verify.')
|
||||
else:
|
||||
messages.warning(request, 'Account created but verification email could not be sent. You can request a new one after logging in.')
|
||||
messages.warning(request, 'Account created. Email verification failed - try again later.')
|
||||
|
||||
return redirect('authentication:login')
|
||||
else:
|
||||
@ -173,7 +173,7 @@ def register_view(request):
|
||||
user.save()
|
||||
|
||||
login(request, user)
|
||||
messages.success(request, f'Welcome {user.username}! Your account has been created successfully.')
|
||||
messages.success(request, f'Welcome {user.username}!')
|
||||
return redirect('core:homepage')
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating account for {email}: {str(e)}")
|
||||
@ -185,7 +185,6 @@ def register_view(request):
|
||||
def logout_view(request):
|
||||
"""User logout view"""
|
||||
logout(request)
|
||||
messages.success(request, 'You have been logged out successfully')
|
||||
return redirect('core:homepage')
|
||||
|
||||
|
||||
@ -337,7 +336,7 @@ def reset_password_view(request, token):
|
||||
# Mark token as used
|
||||
reset_token.mark_as_used()
|
||||
|
||||
messages.success(request, 'Your password has been reset successfully. You can now log in.')
|
||||
messages.success(request, 'Password reset. You can now log in.')
|
||||
return redirect('authentication:login')
|
||||
|
||||
return render(request, 'authentication/reset_password.html', {'token': token})
|
||||
@ -359,7 +358,7 @@ def verify_email_view(request, token):
|
||||
# Mark token as used
|
||||
verification_token.mark_as_used()
|
||||
|
||||
messages.success(request, 'Email verified successfully! You can now log in.')
|
||||
messages.success(request, 'Email verified. You can now log in.')
|
||||
return redirect('authentication:login')
|
||||
|
||||
|
||||
@ -384,7 +383,7 @@ def resend_verification_view(request):
|
||||
|
||||
# Send new verification email
|
||||
if send_verification_email(user):
|
||||
messages.success(request, 'Verification email sent. Please check your inbox.')
|
||||
messages.success(request, 'Verification email sent.')
|
||||
else:
|
||||
messages.error(request, 'Unable to send verification email at this time.')
|
||||
|
||||
|
||||
@ -79,16 +79,22 @@ def wallet_topup_view(request):
|
||||
return redirect('wallet:wallet_topup')
|
||||
except Exception as e:
|
||||
logger.error(f"Checkout session creation failed for user {request.user.id}: {e}")
|
||||
messages.error(request, 'Unable to process payment at this time. Please try again.')
|
||||
messages.error(request, 'Payment failed. Try again.')
|
||||
return redirect('wallet:wallet_topup')
|
||||
|
||||
return render(request, 'wallet/wallet_topup.html')
|
||||
|
||||
|
||||
@login_required
|
||||
@ratelimit(key='user', rate='10/m', method='GET', block=False)
|
||||
def wallet_topup_success_view(request):
|
||||
"""Payment success page with automatic payment verification"""
|
||||
# Check authentication first and clear any messages if redirecting to login
|
||||
if not request.user.is_authenticated:
|
||||
# Clear any existing messages to prevent them from showing on login page
|
||||
storage = messages.get_messages(request)
|
||||
storage.used = True
|
||||
return redirect('authentication:login')
|
||||
|
||||
# Check if rate limited
|
||||
if getattr(request, 'limited', False):
|
||||
logger.warning(f"Payment success page rate limit exceeded for user {request.user.id}")
|
||||
@ -99,7 +105,7 @@ def wallet_topup_success_view(request):
|
||||
|
||||
if not session_id:
|
||||
logger.warning(f"No session ID provided for user {request.user.id}")
|
||||
messages.error(request, 'No payment session found. Please contact support if you completed a payment.')
|
||||
messages.error(request, 'Payment session not found.')
|
||||
return redirect('wallet:wallet')
|
||||
|
||||
# Verify payment directly with Stripe API
|
||||
@ -111,10 +117,10 @@ def wallet_topup_success_view(request):
|
||||
|
||||
if result['success']:
|
||||
if result['processed']:
|
||||
messages.success(request, f'Payment successful! {result["amount"]} AED has been added to your wallet.')
|
||||
messages.success(request, f'{result["amount"]} AED added to wallet.')
|
||||
logger.info(f"Payment verified and wallet updated for user {request.user.id}")
|
||||
else:
|
||||
messages.info(request, 'Payment already processed. Your wallet balance is up to date.')
|
||||
messages.info(request, 'Payment already processed.')
|
||||
logger.info(f"Payment already processed for session {session_id}")
|
||||
else:
|
||||
messages.warning(request, 'Payment verification failed. Please contact support.')
|
||||
@ -122,15 +128,21 @@ def wallet_topup_success_view(request):
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error verifying payment for user {request.user.id}: {e}")
|
||||
messages.error(request, 'Unable to verify payment. Please contact support if you completed a payment.')
|
||||
messages.error(request, 'Payment verification failed.')
|
||||
|
||||
return redirect('wallet:wallet')
|
||||
|
||||
|
||||
@login_required
|
||||
def wallet_topup_cancel_view(request):
|
||||
"""Payment cancel page"""
|
||||
messages.info(request, 'Payment was cancelled. No charges were made.')
|
||||
# Check authentication first and clear any messages if redirecting to login
|
||||
if not request.user.is_authenticated:
|
||||
# Clear any existing messages to prevent them from showing on login page
|
||||
storage = messages.get_messages(request)
|
||||
storage.used = True
|
||||
return redirect('authentication:login')
|
||||
|
||||
messages.info(request, 'Payment cancelled.')
|
||||
return redirect('wallet:wallet_topup')
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user