""" Notification Settings Views Provides admin interface for configuring notification preferences. """ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.core.exceptions import PermissionDenied from django.shortcuts import render, redirect, get_object_or_404 from django.http import JsonResponse from django.views.decorators.http import require_POST from apps.organizations.models import Hospital from .settings_models import HospitalNotificationSettings, NotificationSettingsLog def can_manage_notifications(user): """Check if user can manage notification settings""" if user.is_superuser: return True if hasattr(user, 'is_px_admin') and user.is_px_admin(): return True if hasattr(user, 'is_hospital_admin') and user.is_hospital_admin(): return True return False @login_required def notification_settings_view(request, hospital_id=None): """ Notification settings configuration page. Allows hospital admins to toggle notification preferences for different events and channels. """ # Check permission if not can_manage_notifications(request.user): raise PermissionDenied("You do not have permission to manage notification settings.") # Get hospital - if superuser, can view any; otherwise only their hospital if request.user.is_superuser and hospital_id: hospital = get_object_or_404(Hospital, id=hospital_id) else: hospital = request.user.hospital hospital_id = hospital.id # Get or create settings settings = HospitalNotificationSettings.get_for_hospital(hospital_id) # Group settings by category for display notification_categories = [ { 'key': 'complaint', 'name': 'Complaint Notifications', 'icon': 'bi-exclamation-triangle', 'description': 'Notifications related to complaint lifecycle', 'events': [ { 'key': 'complaint_acknowledgment', 'name': 'Complaint Acknowledgment', 'description': 'Sent to patient when complaint is acknowledged', 'icon': 'bi-check-circle', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'complaint_assigned', 'name': 'Complaint Assigned', 'description': 'Sent to staff when complaint is assigned to them', 'icon': 'bi-person-check', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'complaint_status_changed', 'name': 'Complaint Status Changed', 'description': 'Sent when complaint status is updated', 'icon': 'bi-arrow-repeat', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'complaint_resolved', 'name': 'Complaint Resolved', 'description': 'Sent to patient when complaint is resolved', 'icon': 'bi-check2-all', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'complaint_closed', 'name': 'Complaint Closed', 'description': 'Sent to patient when complaint is closed', 'icon': 'bi-x-circle', 'channels': ['email', 'sms', 'whatsapp'] }, ] }, { 'key': 'explanation', 'name': 'Explanation Notifications', 'icon': 'bi-chat-left-text', 'description': 'Notifications for staff explanation workflow', 'events': [ { 'key': 'explanation_requested', 'name': 'Explanation Requested', 'description': 'Sent to staff when explanation is requested', 'icon': 'bi-question-circle', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'explanation_reminder', 'name': 'Explanation Reminder (24h)', 'description': 'Reminder sent 24h before SLA deadline', 'icon': 'bi-clock-history', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'explanation_overdue', 'name': 'Explanation Overdue/Escalation', 'description': 'Sent to manager when explanation is overdue', 'icon': 'bi-exclamation-diamond', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'explanation_received', 'name': 'Explanation Received', 'description': 'Sent to assignee when staff submits explanation', 'icon': 'bi-inbox', 'channels': ['email', 'sms', 'whatsapp'] }, ], 'extra_settings': [ { 'key': 'explanation_manager_cc', 'name': 'Manager CC on Explanation Request', 'description': 'CC manager when explanation is requested from staff', 'icon': 'bi-person-badge' } ] }, { 'key': 'survey', 'name': 'Survey Notifications', 'icon': 'bi-clipboard-check', 'description': 'Notifications for patient surveys', 'events': [ { 'key': 'survey_invitation', 'name': 'Survey Invitation', 'description': 'Initial survey invitation sent to patient', 'icon': 'bi-envelope-open', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'survey_reminder', 'name': 'Survey Reminder', 'description': 'Reminder for patients to complete survey', 'icon': 'bi-bell', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'survey_completed', 'name': 'Survey Completed', 'description': 'Sent to admin when survey is completed', 'icon': 'bi-check-circle-fill', 'channels': ['email', 'sms'] }, ] }, { 'key': 'action', 'name': 'Action Plan Notifications', 'icon': 'bi-list-check', 'description': 'Notifications for action plan assignments', 'events': [ { 'key': 'action_assigned', 'name': 'Action Assigned', 'description': 'Sent to staff when action is assigned', 'icon': 'bi-person-plus', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'action_due_soon', 'name': 'Action Due Soon', 'description': 'Reminder when action is approaching deadline', 'icon': 'bi-calendar-event', 'channels': ['email', 'sms'] }, { 'key': 'action_overdue', 'name': 'Action Overdue', 'description': 'Alert when action is past deadline', 'icon': 'bi-calendar-x', 'channels': ['email', 'sms'] }, ] }, { 'key': 'sla', 'name': 'SLA Notifications', 'icon': 'bi-stopwatch', 'description': 'Notifications for SLA monitoring', 'events': [ { 'key': 'sla_reminder', 'name': 'SLA Reminder', 'description': 'Reminder before SLA breach', 'icon': 'bi-clock', 'channels': ['email', 'sms'] }, { 'key': 'sla_breach', 'name': 'SLA Breach Alert', 'description': 'Alert when SLA is breached', 'icon': 'bi-exclamation-octagon', 'channels': ['email', 'sms', 'whatsapp'] }, ] }, { 'key': 'onboarding', 'name': 'Onboarding Notifications', 'icon': 'bi-person-plus', 'description': 'Notifications for user onboarding and acknowledgements', 'events': [ { 'key': 'onboarding_invitation', 'name': 'Onboarding Invitation', 'description': 'Sent to new provisional users to complete registration', 'icon': 'bi-envelope-plus', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'onboarding_reminder', 'name': 'Onboarding Reminder', 'description': 'Reminder to complete onboarding before invitation expires', 'icon': 'bi-bell', 'channels': ['email', 'sms', 'whatsapp'] }, { 'key': 'onboarding_completion', 'name': 'Onboarding Completion', 'description': 'Sent to admins when user completes onboarding', 'icon': 'bi-check-circle-fill', 'channels': ['email', 'sms'] }, ] }, ] # Get recent change logs change_logs = NotificationSettingsLog.objects.filter( hospital=hospital ).select_related('changed_by').order_by('-created_at')[:10] context = { 'hospital': hospital, 'settings': settings, 'categories': notification_categories, 'change_logs': change_logs, 'is_superuser': request.user.is_superuser, } return render(request, 'notifications/settings.html', context) @login_required @require_POST def notification_settings_update(request, hospital_id=None): """ Update notification settings via AJAX or form POST. """ # Check permission if not can_manage_notifications(request.user): raise PermissionDenied("You do not have permission to manage notification settings.") # Get hospital if request.user.is_superuser and hospital_id: hospital = get_object_or_404(Hospital, id=hospital_id) else: hospital = request.user.hospital settings = HospitalNotificationSettings.get_for_hospital(hospital.id) # Handle master switch if 'notifications_enabled' in request.POST: old_value = settings.notifications_enabled new_value = request.POST.get('notifications_enabled') == 'on' settings.notifications_enabled = new_value settings.save() NotificationSettingsLog.objects.create( hospital=hospital, changed_by=request.user, field_name='notifications_enabled', old_value=old_value, new_value=new_value ) if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'message': f"Notifications {'enabled' if new_value else 'disabled'}" }) messages.success(request, f"Notifications {'enabled' if new_value else 'disabled'}") return redirect('notifications:settings_with_hospital', hospital_id=hospital.id) # Handle individual toggle field_name = request.POST.get('field') value = request.POST.get('value') == 'true' if field_name and hasattr(settings, field_name): old_value = getattr(settings, field_name) setattr(settings, field_name, value) settings.save() # Log the change NotificationSettingsLog.objects.create( hospital=hospital, changed_by=request.user, field_name=field_name, old_value=old_value, new_value=value ) if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': True, 'field': field_name, 'value': value }) messages.success(request, "Setting updated successfully") else: if request.headers.get('X-Requested-With') == 'XMLHttpRequest': return JsonResponse({ 'success': False, 'error': 'Invalid field name' }, status=400) messages.error(request, "Invalid setting") return redirect('notifications:settings_with_hospital', hospital_id=hospital.id) @login_required @require_POST def update_quiet_hours(request, hospital_id=None): """ Update quiet hours settings. """ # Check permission if not can_manage_notifications(request.user): raise PermissionDenied("You do not have permission to manage notification settings.") if request.user.is_superuser and hospital_id: hospital = get_object_or_404(Hospital, id=hospital_id) else: hospital = request.user.hospital settings = HospitalNotificationSettings.get_for_hospital(hospital.id) settings.quiet_hours_enabled = request.POST.get('quiet_hours_enabled') == 'on' settings.quiet_hours_start = request.POST.get('quiet_hours_start', '22:00') settings.quiet_hours_end = request.POST.get('quiet_hours_end', '08:00') settings.save() messages.success(request, "Quiet hours settings updated") return redirect('notifications:settings_with_hospital', hospital_id=hospital.id) @login_required def test_notification(request, hospital_id=None): """ Send a test notification to verify settings. """ # Check permission if not can_manage_notifications(request.user): raise PermissionDenied("You do not have permission to manage notification settings.") if request.user.is_superuser and hospital_id: hospital = get_object_or_404(Hospital, id=hospital_id) else: hospital = request.user.hospital settings = HospitalNotificationSettings.get_for_hospital(hospital.id) channel = request.POST.get('channel', 'email') from .services import NotificationService if channel == 'email' and request.user.email: NotificationService.send_email( email=request.user.email, subject='PX360 Test Notification', message='This is a test notification from PX360.\n\nIf you received this, your email notifications are working correctly.', metadata={'test': True, 'hospital_id': str(hospital.id)} ) messages.success(request, f"Test email sent to {request.user.email}") elif channel == 'sms': phone = request.POST.get('test_phone') if phone: NotificationService.send_sms( phone=phone, message='PX360 Test: Your SMS notifications are configured correctly.', metadata={'test': True, 'hospital_id': str(hospital.id)} ) messages.success(request, f"Test SMS sent to {phone}") else: messages.error(request, "Please provide a phone number") else: messages.error(request, "Invalid channel selected") return redirect('notifications:settings_with_hospital', hospital_id=hospital.id) @login_required def notification_settings_api(request, hospital_id=None): """ API endpoint to get current notification settings as JSON. Useful for AJAX updates and mobile apps. """ if request.user.is_superuser and hospital_id: hospital = get_object_or_404(Hospital, id=hospital_id) else: hospital = request.user.hospital settings = HospitalNotificationSettings.get_for_hospital(hospital.id) # Build response with all settings settings_dict = {} for field in settings._meta.fields: if field.name not in ['id', 'uuid', 'created_at', 'updated_at', 'hospital']: settings_dict[field.name] = getattr(settings, field.name) return JsonResponse({ 'hospital_id': str(hospital.id), 'hospital_name': hospital.name, 'settings': settings_dict })