""" Accounts UI views - Handle HTML rendering for onboarding """ from django.shortcuts import redirect, render from django.contrib.auth.decorators import login_required from django.contrib.auth import get_user_model from django.utils import timezone from django.views.decorators.http import require_http_methods from django.http import JsonResponse from django.contrib import messages from .models import ( AcknowledgementContent, AcknowledgementChecklistItem, UserAcknowledgement, ) from .permissions import IsPXAdmin, CanManageOnboarding, CanViewOnboarding User = get_user_model() # ==================== Onboarding Wizard Views ==================== def onboarding_welcome(request, token=None): """ Welcome page for onboarding wizard """ # If user is already authenticated and not provisional, redirect to dashboard if request.user.is_authenticated and not request.user.is_provisional: return redirect('/') context = { 'page_title': 'Welcome to PX360', } return render(request, 'accounts/onboarding/welcome.html', context) @login_required def onboarding_step_content(request, step): """ Display content step of the onboarding wizard """ user = request.user # Check if user is provisional if not user.is_provisional: return redirect('/') # Get content for user's role content_list = get_wizard_content_for_user(user) # Get current step content try: current_content = content_list[step - 1] except IndexError: # Step doesn't exist, go to checklist return redirect('/accounts/onboarding/wizard/checklist/') # Get completed steps completed_steps = user.wizard_completed_steps or [] # Calculate progress progress_percentage = int((len(completed_steps) / len(content_list)) * 100) # Get previous and next steps previous_step = step - 1 if step > 1 else None next_step = step + 1 if step < len(content_list) else None context = { 'page_title': f'Onboarding - Step {step}', 'step': step, 'content': content_list, 'current_content': current_content, 'completed_steps': completed_steps, 'progress_percentage': progress_percentage, 'previous_step': previous_step, 'next_step': next_step, 'user': user, } return render(request, 'accounts/onboarding/step_content.html', context) @login_required def onboarding_step_checklist(request): """ Display checklist step of the onboarding wizard """ user = request.user # Check if user is provisional if not user.is_provisional: return redirect('/dashboard/') # Get checklist items for user's role checklist_items = get_checklist_items_for_user(user) # Get acknowledged items acknowledged_ids = UserAcknowledgement.objects.filter( user=user, is_acknowledged=True ).values_list('checklist_item_id', flat=True) # Add acknowledgement status to items for item in checklist_items: item.is_acknowledged = item.id in acknowledged_ids # Get required items IDs required_items = [ str(item.id) for item in checklist_items if item.is_required ] # Calculate progress total_count = len(checklist_items) acknowledged_count = len([i for i in checklist_items if i.is_acknowledged]) progress_percentage = int((acknowledged_count / total_count) * 100) if total_count > 0 else 0 context = { 'page_title': 'Acknowledgement Checklist', 'checklist_items': checklist_items, 'acknowledged_count': acknowledged_count, 'total_count': total_count, 'progress_percentage': progress_percentage, 'required_items_json': required_items, 'user': user, } return render(request, 'accounts/onboarding/step_checklist.html', context) @login_required def onboarding_step_activation(request): """ Display account activation step """ user = request.user # Check if user is provisional if not user.is_provisional: return redirect('/dashboard/') # Check if all required acknowledgements are completed required_items = get_checklist_items_for_user(user).filter(is_required=True) acknowledged_items = UserAcknowledgement.objects.filter( user=user, checklist_item__in=required_items, is_acknowledged=True ) if required_items.count() != acknowledged_items.count(): messages.warning(request, 'Please complete all required acknowledgements first.') return redirect('/accounts/onboarding/wizard/checklist/') context = { 'page_title': 'Account Activation', 'user': user, } return render(request, 'accounts/onboarding/step_activation.html', context) @login_required def onboarding_complete(request): """ Display completion page """ user = request.user # Check if user is not provisional (i.e., completed onboarding) if user.is_provisional: return redirect('/accounts/onboarding/wizard/step/1/') context = { 'page_title': 'Onboarding Complete', 'user': user, } return render(request, 'accounts/onboarding/complete.html', context) # ==================== Provisional User Management Views ==================== @login_required @require_http_methods(["GET", "POST"]) def provisional_user_list(request): """ List and manage provisional users (PX Admin only) """ if not request.user.is_px_admin(): messages.error(request, 'You do not have permission to view this page.') return redirect('/dashboard/') if request.method == 'POST': # Handle create provisional user from .serializers import ProvisionalUserSerializer from .services import OnboardingService, EmailService serializer = ProvisionalUserSerializer(data=request.POST) if serializer.is_valid(): user_data = serializer.validated_data.copy() roles = request.POST.getlist('roles', []) # Remove roles from user_data (not a User model field) user_data.pop('roles', None) # Create provisional user user = OnboardingService.create_provisional_user(user_data) # Assign roles for role_name in roles: try: from .models import Role role = Role.objects.get(name=role_name) user.groups.add(role.group) except Role.DoesNotExist: pass # Send invitation email EmailService.send_invitation_email(user, request) messages.success(request, f'Provisional user {user.email} created successfully.') return redirect('accounts:provisional-user-list') else: messages.error(request, 'Failed to create provisional user. Please check the form.') # Get all provisional users provisional_users = User.objects.filter( is_provisional=True ).select_related('hospital', 'department').order_by('-created_at') # Calculate statistics total_count = provisional_users.count() completed_count = provisional_users.filter(acknowledgement_completed_at__isnull=False).count() in_progress_count = total_count - completed_count # Get available roles from .models import Role roles = Role.objects.all() # Get hospitals and departments for the form from apps.organizations.models import Hospital, Department hospitals = Hospital.objects.filter(status='active').order_by('name') departments = Department.objects.filter(status='active').order_by('name') context = { 'page_title': 'Provisional Users', 'provisional_users': provisional_users, 'roles': roles, 'hospitals': hospitals, 'departments': departments, 'total_count': total_count, 'completed_count': completed_count, 'in_progress_count': in_progress_count, } return render(request, 'accounts/onboarding/provisional_list.html', context) @login_required def provisional_user_progress(request, user_id): """ View onboarding progress for a specific user """ if not (request.user.is_px_admin() or request.user.id == user_id): messages.error(request, 'You do not have permission to view this page.') return redirect('/dashboard/') user = User.objects.get(id=user_id) # Get checklist items checklist_items = get_checklist_items_for_user(user) # Get acknowledged items acknowledged_items = UserAcknowledgement.objects.filter( user=user, is_acknowledged=True ).select_related('checklist_item') # Create a lookup dict: checklist_item_id -> acknowledged_at timestamp acknowledged_timestamps = {} for ack in acknowledged_items: if ack.checklist_item_id: acknowledged_timestamps[ack.checklist_item_id] = ack.acknowledged_at # Get logs from .models import UserProvisionalLog logs = UserProvisionalLog.objects.filter( user=user ).order_by('-created_at') # Calculate progress total_items = checklist_items.filter(is_required=True).count() acknowledged_count = acknowledged_items.filter( checklist_item__is_required=True ).count() progress_percentage = int((acknowledged_count / total_items) * 100) if total_items > 0 else 0 remaining_count = total_items - acknowledged_count # Attach acknowledged_at timestamp to each checklist item checklist_items_with_timestamps = [] for item in checklist_items: item_dict = { 'item': item, 'acknowledged_at': acknowledged_timestamps.get(item.id), } checklist_items_with_timestamps.append(item_dict) context = { 'page_title': f'Onboarding Progress - {user.email}', 'user': user, 'checklist_items': checklist_items_with_timestamps, 'logs': logs, 'total_items': total_items, 'acknowledged_count': acknowledged_count, 'remaining_count': remaining_count, 'progress_percentage': progress_percentage, } return render(request, 'accounts/onboarding/progress_detail.html', context) # ==================== Acknowledgement Management Views ==================== @login_required def acknowledgement_content_list(request): """ List acknowledgement content (PX Admin only) """ if not request.user.is_px_admin(): messages.error(request, 'You do not have permission to view this page.') return redirect('/dashboard/') # Get all content content_list = AcknowledgementContent.objects.all().order_by('role', 'order') context = { 'page_title': 'Acknowledgement Content', 'content_list': content_list, } return render(request, 'accounts/onboarding/content_list.html', context) @login_required def acknowledgement_checklist_list(request): """ List acknowledgement checklist items (PX Admin only) """ if not request.user.is_px_admin(): messages.error(request, 'You do not have permission to view this page.') return redirect('/dashboard/') # Get all checklist items checklist_items = AcknowledgementChecklistItem.objects.select_related( 'content' ).order_by('role', 'order') context = { 'page_title': 'Acknowledgement Checklist Items', 'checklist_items': checklist_items, } return render(request, 'accounts/onboarding/checklist_list.html', context) # ==================== Helper Functions ==================== def get_wizard_content_for_user(user): """ Get wizard content based on user's role """ from .models import Role # Get user's role user_role = None if user.groups.filter(name='PX Admin').exists(): user_role = 'px_admin' elif user.groups.filter(name='Hospital Admin').exists(): user_role = 'hospital_admin' elif user.groups.filter(name='Department Manager').exists(): user_role = 'department_manager' elif user.groups.filter(name='Staff').exists(): user_role = 'staff' elif user.groups.filter(name='Physician').exists(): user_role = 'physician' # Get content for role or general content if user_role: content = AcknowledgementContent.objects.filter( role__in=[user_role, 'all'] ) else: content = AcknowledgementContent.objects.filter(role='all') return content.filter(is_active=True).order_by('order') def get_checklist_items_for_user(user): """ Get checklist items based on user's role """ from .models import Role # Get user's role user_role = None if user.groups.filter(name='PX Admin').exists(): user_role = 'px_admin' elif user.groups.filter(name='Hospital Admin').exists(): user_role = 'hospital_admin' elif user.groups.filter(name='Department Manager').exists(): user_role = 'department_manager' elif user.groups.filter(name='Staff').exists(): user_role = 'staff' elif user.groups.filter(name='Physician').exists(): user_role = 'physician' # Get checklist items for role or general items if user_role: items = AcknowledgementChecklistItem.objects.filter( role__in=[user_role, 'all'] ) else: items = AcknowledgementChecklistItem.objects.filter(role='all') return items.filter(is_active=True).order_by('order')