diff --git a/agents/templates/agents/agent_chat.html b/agents/templates/agents/agent_chat.html index 886a7f0..0f554f0 100644 --- a/agents/templates/agents/agent_chat.html +++ b/agents/templates/agents/agent_chat.html @@ -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); +} {% endblock %} @@ -584,6 +656,22 @@ document.body.setAttribute('data-session-expires', '{{ chat_session.expires_at.i {% endif %} + + +
@@ -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/'; +} {% endblock %} \ No newline at end of file diff --git a/agents/views.py b/agents/views.py index c63ed6e..fcdc3db 100644 --- a/agents/views.py +++ b/agents/views.py @@ -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') diff --git a/authentication/views.py b/authentication/views.py index 291e29a..5b9130b 100644 --- a/authentication/views.py +++ b/authentication/views.py @@ -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.') diff --git a/wallet/views.py b/wallet/views.py index e119189..da275f0 100644 --- a/wallet/views.py +++ b/wallet/views.py @@ -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')