""" Complaints forms """ import os from django import forms from django.db import models from django.core.exceptions import ValidationError from django.core.validators import validate_email from django.utils.translation import gettext_lazy as _ from apps.complaints.models import ( Complaint, ComplaintCategory, ComplaintSource, ComplaintSourceType, ComplaintStatus, ComplaintType, Inquiry, ComplaintSLAConfig, EscalationRule, ComplaintThreshold, ) from apps.core.models import PriorityChoices, SeverityChoices from apps.organizations.models import Department, Hospital, Patient, Staff class MultiFileInput(forms.FileInput): """ Custom FileInput widget that supports multiple file uploads. Unlike standard FileInput which only supports single files, this widget allows users to upload multiple files at once. """ def __init__(self, attrs=None): # Call parent's __init__ first to avoid Django's 'multiple' check super().__init__(attrs) # Add 'multiple' attribute after initialization self.attrs['multiple'] = 'multiple' def value_from_datadict(self, data, files, name): """ Get all uploaded files for a given field name. Returns a list of uploaded files instead of a single file. """ if name in files: return files.getlist(name) return [] class PublicComplaintForm(forms.ModelForm): """ Simplified public complaint submission form. Key changes for AI-powered classification: - Fewer required fields (simplified for public users) - Severity and priority removed (AI will determine these automatically) - Only essential information collected - Updated with new fields: relation_to_patient, patient_name, national_id, incident_date, staff_name, expected_result """ # Contact Information complainant_name = forms.CharField( label=_("Complainant Name"), max_length=200, required=True, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': _('Your full name') } ) ) relation_to_patient = forms.ChoiceField( label=_("Relation to Patient"), choices=[ ('patient', 'Patient'), ('relative', 'Relative'), ], required=True, widget=forms.Select( attrs={ 'class': 'form-control' } ) ) email = forms.EmailField( label=_("Email Address"), required=False, widget=forms.EmailInput( attrs={ 'class': 'form-control', 'placeholder': _('your@email.com') } ) ) mobile_number = forms.CharField( label=_("Mobile Number"), max_length=20, required=True, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': _('Your mobile number') } ) ) # Patient Information patient_name = forms.CharField( label=_("Patient Name"), max_length=200, required=True, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': _('Name of the patient involved') } ) ) national_id = forms.CharField( label=_("National ID/ Iqama No."), max_length=20, required=True, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': _('Saudi National ID or Iqama number') } ) ) incident_date = forms.DateField( label=_("Incident Date"), required=True, widget=forms.DateInput( attrs={ 'class': 'form-control', 'type': 'date' } ) ) # Hospital and Department hospital = forms.ModelChoiceField( label=_("Hospital"), queryset=Hospital.objects.filter(status='active').order_by('name'), empty_label=_("Select Hospital"), required=True, widget=forms.Select( attrs={ 'class': 'form-control', 'id': 'hospital_select', 'data-action': 'load-departments' } ) ) department = forms.ModelChoiceField( label=_("Department (Optional)"), queryset=Department.objects.none(), empty_label=_("Select Department"), required=False, widget=forms.Select( attrs={ 'class': 'form-control', 'id': 'department_select' } ) ) # Complaint Details - Location Hierarchy location = forms.ModelChoiceField( label=_("Location"), queryset=None, empty_label=_("Select Location"), required=True, widget=forms.Select( attrs={ 'class': 'form-control', 'id': 'location_select', 'data-action': 'load-sections' } ) ) main_section = forms.ModelChoiceField( label=_("Section"), queryset=None, empty_label=_("Select Section"), required=True, widget=forms.Select( attrs={ 'class': 'form-control', 'id': 'main_section_select', 'data-action': 'load-subsections' } ) ) subsection = forms.ModelChoiceField( label=_("Subsection"), queryset=None, empty_label=_("Select Subsection"), required=True, widget=forms.Select( attrs={ 'class': 'form-control', 'id': 'subsection_select' } ) ) staff_name = forms.CharField( label=_("Staff Involved"), max_length=200, required=False, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': _('Name of staff member involved (if known)') } ) ) complaint_details = forms.CharField( label=_("Complaint Details"), required=True, widget=forms.Textarea( attrs={ 'class': 'form-control', 'rows': 6, 'placeholder': _('Please describe your complaint in detail. Our AI system will analyze and prioritize your complaint accordingly.') } ) ) expected_result = forms.CharField( label=_("Expected Complaint Result"), required=False, widget=forms.Textarea( attrs={ 'class': 'form-control', 'rows': 3, 'placeholder': _('What do you expect as a resolution?') } ) ) # Hidden fields - these will be set by view or AI severity = forms.ChoiceField( label=_("Severity"), choices=SeverityChoices.choices, initial=SeverityChoices.MEDIUM, required=False, widget=forms.HiddenInput() ) priority = forms.ChoiceField( label=_("Priority"), choices=PriorityChoices.choices, initial=PriorityChoices.MEDIUM, required=False, widget=forms.HiddenInput() ) # Source type - always external for public complaints complaint_source_type = forms.ChoiceField( label=_("Complaint Source Type"), choices=ComplaintSourceType.choices, initial=ComplaintSourceType.EXTERNAL, required=False, widget=forms.HiddenInput() ) # File uploads attachments = forms.FileField( label=_("Attach Documents (Optional)"), required=False, widget=MultiFileInput( attrs={ 'class': 'form-control', 'accept': 'image/*,.pdf,.doc,.docx' } ), help_text=_('You can upload images, PDFs, or Word documents (max 10MB each)') ) class Meta: model = Complaint fields = [ 'complainant_name', 'email', 'mobile_number', 'hospital', 'relation_to_patient', 'patient_name', 'national_id', 'incident_date', 'location', 'main_section', 'subsection', 'staff_name', 'complaint_details', 'expected_result', 'severity', 'priority', 'complaint_source_type' ] # Note: 'attachments' is not in fields because Complaint model doesn't have this field. # Attachments are handled separately via ComplaintAttachment model in the view. def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) from apps.organizations.models import Location, MainSection, SubSection # Initialize cascading dropdowns with empty querysets self.fields['main_section'].queryset = MainSection.objects.none() self.fields['subsection'].queryset = SubSection.objects.none() # Load all locations (no filtering needed) self.fields['location'].queryset = Location.objects.all().order_by('name_en') # Check both initial data and POST data for location to load sections location_id = None if 'location' in self.initial: location_id = self.initial['location'] elif 'location' in self.data: location_id = self.data['location'] if location_id: # Filter sections based on selected location from apps.organizations.models import SubSection available_sections = SubSection.objects.filter( location_id=location_id ).values_list('main_section_id', flat=True).distinct() self.fields['main_section'].queryset = MainSection.objects.filter( id__in=available_sections ).order_by('name_en') # Load subsections if section is selected section_id = None if 'main_section' in self.initial: section_id = self.initial['main_section'] elif 'main_section' in self.data: section_id = self.data['main_section'] if section_id: self.fields['subsection'].queryset = SubSection.objects.filter( location_id=location_id, main_section_id=section_id ).order_by('name_en') # Also filter departments based on hospital if provided hospital_id = None if 'hospital' in self.initial: hospital_id = self.initial['hospital'] elif 'hospital' in self.data: hospital_id = self.data['hospital'] if hospital_id: # Filter departments self.fields['department'].queryset = Department.objects.filter( hospital_id=hospital_id, status='active' ).order_by('name') def clean_mobile_number(self): """Validate mobile number format""" mobile_number = self.cleaned_data.get('mobile_number') # Remove spaces and dashes mobile_number = mobile_number.replace(' ', '').replace('-', '') # Validate Saudi mobile format (05xxxxxxxx) if not mobile_number.startswith('05') or len(mobile_number) != 10: raise ValidationError(_('Please enter a valid Saudi mobile number (10 digits starting with 05)')) return mobile_number def clean_national_id(self): """Validate National ID/Iqama format""" national_id = self.cleaned_data.get('national_id') # Remove spaces national_id = national_id.replace(' ', '') # Validate it's 10 digits if len(national_id) != 10 or not national_id.isdigit(): raise ValidationError(_('Please enter a valid National ID or Iqama number (10 digits)')) return national_id def clean_incident_date(self): """Validate incident date is not in the future""" incident_date = self.cleaned_data.get('incident_date') from datetime import date if incident_date and incident_date > date.today(): raise ValidationError(_('Incident date cannot be in the future')) return incident_date def clean_attachments(self): """Validate file attachments""" files = self.files.getlist('attachments') # Check file count if len(files) > 5: raise ValidationError(_('Maximum 5 files allowed')) # Check each file for file in files: # Check file size (10MB limit) if file.size > 10 * 1024 * 1024: raise ValidationError(_('File size must be less than 10MB')) # Check file type allowed_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.pdf', '.doc', '.docx'] ext = os.path.splitext(file.name)[1].lower() if ext not in allowed_extensions: raise ValidationError(_('Allowed file types: JPG, PNG, GIF, PDF, DOC, DOCX')) return files def clean(self): """Custom cross-field validation""" cleaned_data = super().clean() # Basic validation - all required fields are already validated by field definitions # This method is kept for future custom cross-field validation needs return cleaned_data class ComplaintForm(forms.ModelForm): """ Form for creating complaints by authenticated users. Updated to use location hierarchy (Location, Section, Subsection). Includes new fields for detailed patient information and complaint type. Uses cascading dropdowns for location selection. """ # Complaint Type complaint_type = forms.ChoiceField( label=_("Feedback Type"), choices=ComplaintType.choices, initial=ComplaintType.COMPLAINT, required=False, widget=forms.HiddenInput() ) # Source type complaint_source_type = forms.ChoiceField( label=_("Complaint Source Type"), choices=ComplaintSourceType.choices, initial=ComplaintSourceType.EXTERNAL, required=False, widget=forms.Select(attrs={'class': 'form-select', 'id': 'complaintSourceType'}) ) # Patient Information (text-based fields only) relation_to_patient = forms.ChoiceField( label=_("Relation to Patient"), choices=[ ('patient', 'Patient'), ('relative', 'Relative'), ], required=True, widget=forms.Select(attrs={'class': 'form-select', 'id': 'relationToPatient'}) ) patient_name = forms.CharField( label=_("Patient Name"), max_length=200, required=True, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Name of the patient involved')}) ) national_id = forms.CharField( label=_("National ID/Iqama No."), max_length=20, required=True, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Saudi National ID or Iqama number')}) ) incident_date = forms.DateField( label=_("Incident Date"), required=True, widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}) ) hospital = forms.ModelChoiceField( label=_("Hospital"), queryset=Hospital.objects.filter(status='active'), empty_label=_("Select Hospital"), required=True, widget=forms.Select(attrs={'class': 'form-select', 'id': 'hospitalSelect'}) ) department = forms.ModelChoiceField( label=_("Department"), queryset=Department.objects.none(), empty_label=_("Select Department"), required=False, widget=forms.Select(attrs={'class': 'form-select', 'id': 'departmentSelect'}) ) staff = forms.ModelChoiceField( label=_("Staff"), queryset=Staff.objects.none(), empty_label=_("Select Staff"), required=False, widget=forms.Select(attrs={'class': 'form-select', 'id': 'staffSelect'}) ) encounter_id = forms.CharField( label=_("Encounter ID"), required=False, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Optional encounter/visit ID')}) ) # Location Hierarchy Fields location = forms.ModelChoiceField( label=_("Location"), queryset=None, empty_label=_("Select Location"), required=True, widget=forms.Select(attrs={ 'class': 'form-select', 'id': 'locationSelect', 'data-action': 'load-sections' }) ) main_section = forms.ModelChoiceField( label=_("Section"), queryset=None, empty_label=_("Select Section"), required=True, widget=forms.Select(attrs={ 'class': 'form-select', 'id': 'mainSectionSelect', 'data-action': 'load-subsections' }) ) subsection = forms.ModelChoiceField( label=_("Subsection"), queryset=None, empty_label=_("Select Subsection"), required=True, widget=forms.Select(attrs={'class': 'form-select', 'id': 'subsectionSelect'}) ) staff_name = forms.CharField( label=_("Staff Involved"), max_length=200, required=False, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Name of staff member involved (if known)')}) ) description = forms.CharField( label=_("Description"), required=True, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 6, 'placeholder': _('Detailed description of complaint...')}) ) expected_result = forms.CharField( label=_("Expected Complaint Result"), required=False, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': _('What do you expect as a resolution?')}) ) class Meta: model = Complaint fields = [ 'complaint_type', 'complaint_source_type', 'relation_to_patient', 'patient_name', 'national_id', 'incident_date', 'hospital', 'department', 'location', 'main_section', 'subsection', 'staff', 'staff_name', 'encounter_id', 'description', 'expected_result' ] def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) from apps.organizations.models import Location, MainSection, SubSection # Initialize cascading dropdowns with empty querysets self.fields['main_section'].queryset = MainSection.objects.none() self.fields['subsection'].queryset = SubSection.objects.none() # Load all locations (no filtering needed) self.fields['location'].queryset = Location.objects.all().order_by('name_en') # Check both initial data and POST data for location to load sections location_id = None if 'location' in self.initial: location_id = self.initial['location'] elif 'location' in self.data: location_id = self.data['location'] if location_id: # Filter sections based on selected location from apps.organizations.models import SubSection available_sections = SubSection.objects.filter( location_id=location_id ).values_list('main_section_id', flat=True).distinct() self.fields['main_section'].queryset = MainSection.objects.filter( id__in=available_sections ).order_by('name_en') # Load subsections if section is selected section_id = None if 'main_section' in self.initial: section_id = self.initial['main_section'] elif 'main_section' in self.data: section_id = self.data['main_section'] if section_id: self.fields['subsection'].queryset = SubSection.objects.filter( location_id=location_id, main_section_id=section_id ).order_by('name_en') # Filter hospitals based on user permissions if user and not user.is_px_admin() and user.hospital: self.fields['hospital'].queryset = Hospital.objects.filter(id=user.hospital.id) self.fields['hospital'].initial = user.hospital # Check for hospital selection in both initial data and POST data hospital_id = None if 'hospital' in self.data: hospital_id = self.data.get('hospital') elif 'hospital' in self.initial: hospital_id = self.initial.get('hospital') if hospital_id: # Filter departments based on selected hospital self.fields['department'].queryset = Department.objects.filter( hospital_id=hospital_id, status='active' ).order_by('name') # Filter staff based on selected hospital self.fields['staff'].queryset = Staff.objects.filter( hospital_id=hospital_id, status='active' ).order_by('first_name', 'last_name') class InquiryForm(forms.ModelForm): """ Form for creating inquiries by authenticated users. Similar to ComplaintForm - supports patient search, department filtering, and proper field validation with AJAX support. """ patient = forms.ModelChoiceField( label=_("Patient (Optional)"), queryset=Patient.objects.filter(status='active'), empty_label=_("Select Patient"), required=False, widget=forms.Select(attrs={'class': 'form-select', 'id': 'patientSelect'}) ) hospital = forms.ModelChoiceField( label=_("Hospital"), queryset=Hospital.objects.filter(status='active'), empty_label=_("Select Hospital"), required=True, widget=forms.Select(attrs={'class': 'form-select', 'id': 'hospitalSelect'}) ) department = forms.ModelChoiceField( label=_("Department (Optional)"), queryset=Department.objects.none(), empty_label=_("Select Department"), required=False, widget=forms.Select(attrs={'class': 'form-select', 'id': 'departmentSelect'}) ) category = forms.ChoiceField( label=_("Inquiry Type"), choices=[ ('general', 'General Inquiry'), ('appointment', 'Appointment Related'), ('billing', 'Billing & Insurance'), ('medical_records', 'Medical Records'), ('pharmacy', 'Pharmacy'), ('insurance', 'Insurance'), ('feedback', 'Feedback'), ('other', 'Other'), ], required=True, widget=forms.Select(attrs={'class': 'form-control'}) ) subject = forms.CharField( label=_("Subject"), max_length=200, required=True, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': _('Brief subject')}) ) message = forms.CharField( label=_("Message"), required=True, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 5, 'placeholder': _('Describe your inquiry')}) ) # Contact info for inquiries without patient contact_name = forms.CharField(label=_("Contact Name"), max_length=200, required=False, widget=forms.TextInput(attrs={'class': 'form-control'})) contact_phone = forms.CharField(label=_("Contact Phone"), max_length=20, required=False, widget=forms.TextInput(attrs={'class': 'form-control'})) contact_email = forms.EmailField(label=_("Contact Email"), required=False, widget=forms.EmailInput(attrs={'class': 'form-control'})) class Meta: model = Inquiry fields = ['patient', 'hospital', 'department', 'subject', 'message', 'contact_name', 'contact_phone', 'contact_email'] def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) # Filter hospitals based on user role if user and not user.is_px_admin() and user.hospital: self.fields['hospital'].queryset = Hospital.objects.filter(id=user.hospital.id) self.fields['hospital'].initial = user.hospital self.fields['hospital'].widget.attrs['readonly'] = True # Check for hospital selection in both initial data and POST data hospital_id = None if 'hospital' in self.data: hospital_id = self.data.get('hospital') elif 'hospital' in self.initial: hospital_id = self.initial.get('hospital') if hospital_id: # Filter departments based on selected hospital self.fields['department'].queryset = Department.objects.filter( hospital_id=hospital_id, status='active' ).order_by('name') class SLAConfigForm(forms.ModelForm): """Form for creating and editing SLA configurations""" class Meta: model = ComplaintSLAConfig fields = ['hospital', 'severity', 'priority', 'sla_hours', 'reminder_hours_before', 'is_active'] widgets = { 'hospital': forms.Select(attrs={'class': 'form-select'}), 'severity': forms.Select(attrs={'class': 'form-select'}), 'priority': forms.Select(attrs={'class': 'form-select'}), 'sla_hours': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}), 'reminder_hours_before': forms.NumberInput(attrs={'class': 'form-control', 'min': '0'}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) # Filter hospitals based on user role if user and not user.is_px_admin() and user.hospital: self.fields['hospital'].queryset = Hospital.objects.filter(id=user.hospital.id) self.fields['hospital'].initial = user.hospital self.fields['hospital'].widget.attrs['readonly'] = True def clean(self): cleaned_data = super().clean() hospital = cleaned_data.get('hospital') severity = cleaned_data.get('severity') priority = cleaned_data.get('priority') sla_hours = cleaned_data.get('sla_hours') reminder_hours = cleaned_data.get('reminder_hours_before') # Validate SLA hours is positive if sla_hours and sla_hours <= 0: raise ValidationError({'sla_hours': 'SLA hours must be greater than 0'}) # Validate reminder hours < SLA hours if sla_hours and reminder_hours and reminder_hours >= sla_hours: raise ValidationError({'reminder_hours_before': 'Reminder hours must be less than SLA hours'}) # Check for unique combination (excluding current instance when editing) if hospital and severity and priority: queryset = ComplaintSLAConfig.objects.filter( hospital=hospital, severity=severity, priority=priority ) if self.instance.pk: queryset = queryset.exclude(pk=self.instance.pk) if queryset.exists(): raise ValidationError( 'An SLA configuration for this hospital, severity, and priority already exists.' ) return cleaned_data class EscalationRuleForm(forms.ModelForm): """Form for creating and editing escalation rules""" class Meta: model = EscalationRule fields = [ 'hospital', 'name', 'description', 'escalation_level', 'max_escalation_level', 'trigger_on_overdue', 'trigger_hours_overdue', 'reminder_escalation_enabled', 'reminder_escalation_hours', 'escalate_to_role', 'escalate_to_user', 'severity_filter', 'priority_filter', 'is_active' ] widgets = { 'hospital': forms.Select(attrs={'class': 'form-select'}), 'name': forms.TextInput(attrs={'class': 'form-control'}), 'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), 'escalation_level': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}), 'max_escalation_level': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}), 'trigger_on_overdue': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'trigger_hours_overdue': forms.NumberInput(attrs={'class': 'form-control', 'min': '0'}), 'reminder_escalation_enabled': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'reminder_escalation_hours': forms.NumberInput(attrs={'class': 'form-control', 'min': '0'}), 'escalate_to_role': forms.Select(attrs={'class': 'form-select', 'id': 'escalate_to_role'}), 'escalate_to_user': forms.Select(attrs={'class': 'form-select'}), 'severity_filter': forms.Select(attrs={'class': 'form-select'}), 'priority_filter': forms.Select(attrs={'class': 'form-select'}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) # Filter hospitals based on user role if user and not user.is_px_admin() and user.hospital: self.fields['hospital'].queryset = Hospital.objects.filter(id=user.hospital.id) self.fields['hospital'].initial = user.hospital self.fields['hospital'].widget.attrs['readonly'] = True # Filter users for escalate_to_user field from apps.accounts.models import User if user and user.is_px_admin(): self.fields['escalate_to_user'].queryset = User.objects.filter(is_active=True) elif user and user.hospital: self.fields['escalate_to_user'].queryset = User.objects.filter( is_active=True, hospital=user.hospital ) else: self.fields['escalate_to_user'].queryset = User.objects.none() def clean(self): cleaned_data = super().clean() escalate_to_role = cleaned_data.get('escalate_to_role') escalate_to_user = cleaned_data.get('escalate_to_user') # If role is 'specific_user', user must be specified if escalate_to_role == 'specific_user' and not escalate_to_user: raise ValidationError({'escalate_to_user': 'Please select a user when role is set to Specific User'}) return cleaned_data class ComplaintThresholdForm(forms.ModelForm): """Form for creating and editing complaint thresholds""" class Meta: model = ComplaintThreshold fields = ['hospital', 'threshold_type', 'threshold_value', 'comparison_operator', 'action_type', 'is_active'] widgets = { 'hospital': forms.Select(attrs={'class': 'form-select'}), 'threshold_type': forms.Select(attrs={'class': 'form-select'}), 'threshold_value': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.1'}), 'comparison_operator': forms.Select(attrs={'class': 'form-select'}), 'action_type': forms.Select(attrs={'class': 'form-select'}), 'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}), } def __init__(self, *args, **kwargs): user = kwargs.pop('user', None) super().__init__(*args, **kwargs) # Filter hospitals based on user role if user and not user.is_px_admin() and user.hospital: self.fields['hospital'].queryset = Hospital.objects.filter(id=user.hospital.id) self.fields['hospital'].initial = user.hospital self.fields['hospital'].widget.attrs['readonly'] = True class PublicInquiryForm(forms.Form): """Public inquiry submission form (simpler, for general questions)""" # Contact Information name = forms.CharField( label=_("Name"), max_length=200, required=True, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': _('Your full name') } ) ) phone = forms.CharField( label=_("Phone Number"), max_length=20, required=True, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': _('Your phone number') } ) ) email = forms.EmailField( label=_("Email Address"), required=False, widget=forms.EmailInput( attrs={ 'class': 'form-control', 'placeholder': _('your@email.com') } ) ) # Inquiry Details hospital = forms.ModelChoiceField( label=_("Hospital"), queryset=Hospital.objects.filter(status='active').order_by('name'), empty_label=_("Select Hospital"), required=True, widget=forms.Select(attrs={'class': 'form-control'}) ) category = forms.ChoiceField( label=_("Inquiry Type"), choices=[ ('general', 'General Inquiry'), ('appointment', 'Appointment Related'), ('billing', 'Billing & Insurance'), ('medical_records', 'Medical Records'), ('other', 'Other'), ], required=True, widget=forms.Select(attrs={'class': 'form-control'}) ) subject = forms.CharField( label=_("Subject"), max_length=200, required=True, widget=forms.TextInput( attrs={ 'class': 'form-control', 'placeholder': _('Brief subject') } ) ) message = forms.CharField( label=_("Message"), required=True, widget=forms.Textarea( attrs={ 'class': 'form-control', 'rows': 5, 'placeholder': _('Describe your inquiry') } ) )