""" Forms for the notifications app. """ from django import forms from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError from .models import MessageTemplate, Message, NotificationPreference class MessageTemplateForm(forms.ModelForm): """Form for creating/editing message templates.""" class Meta: model = MessageTemplate fields = [ 'code', 'name', 'channel', 'subject', 'body_en', 'body_ar', 'variables', 'is_active' ] widgets = { 'code': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': _('e.g., appointment_reminder') }), 'name': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': _('Template Name') }), 'channel': forms.Select(attrs={ 'class': 'form-select' }), 'subject': forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': _('Email subject (optional)') }), 'body_en': forms.Textarea(attrs={ 'class': 'form-control', 'rows': 6, 'placeholder': _('Message body in English. Use {variable_name} for variables.') }), 'body_ar': forms.Textarea(attrs={ 'class': 'form-control', 'rows': 6, 'placeholder': _('Message body in Arabic (optional)') }), 'variables': forms.Textarea(attrs={ 'class': 'form-control', 'rows': 3, 'placeholder': _('["patient_name", "appointment_date", "clinic_name"]') }), 'is_active': forms.CheckboxInput(attrs={ 'class': 'form-check-input' }), } def clean_variables(self): """Validate and parse variables JSON.""" import json variables = self.cleaned_data.get('variables') if isinstance(variables, str): try: variables = json.loads(variables) except json.JSONDecodeError: raise ValidationError(_('Invalid JSON format for variables')) if not isinstance(variables, list): raise ValidationError(_('Variables must be a list')) return variables def clean_code(self): """Ensure code is unique.""" code = self.cleaned_data.get('code') # Check if code already exists (excluding current instance) qs = MessageTemplate.objects.filter(code=code) if self.instance.pk: qs = qs.exclude(pk=self.instance.pk) if qs.exists(): raise ValidationError(_('A template with this code already exists')) return code class MessageFilterForm(forms.Form): """Form for filtering messages.""" search = forms.CharField( required=False, widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': _('Search by recipient or content...') }) ) channel = forms.ChoiceField( required=False, choices=[('', _('All Channels'))] + list(Message.Channel.choices), widget=forms.Select(attrs={ 'class': 'form-select' }) ) status = forms.ChoiceField( required=False, choices=[('', _('All Statuses'))] + list(Message.Status.choices), widget=forms.Select(attrs={ 'class': 'form-select' }) ) date_from = forms.DateField( required=False, widget=forms.DateInput(attrs={ 'class': 'form-control', 'type': 'date' }) ) date_to = forms.DateField( required=False, widget=forms.DateInput(attrs={ 'class': 'form-control', 'type': 'date' }) ) template = forms.ModelChoiceField( queryset=MessageTemplate.objects.none(), required=False, widget=forms.Select(attrs={ 'class': 'form-select' }), empty_label=_('All Templates') ) def __init__(self, *args, tenant=None, **kwargs): super().__init__(*args, **kwargs) if tenant: self.fields['template'].queryset = MessageTemplate.objects.filter( tenant=tenant ).order_by('name') class BulkMessageForm(forms.Form): """Form for sending bulk messages.""" channel = forms.ChoiceField( label=_('Channel'), choices=Message.Channel.choices, widget=forms.Select(attrs={ 'class': 'form-select' }) ) use_template = forms.BooleanField( label=_('Use Template'), required=False, initial=True, widget=forms.CheckboxInput(attrs={ 'class': 'form-check-input', 'id': 'useTemplateCheckbox' }) ) template = forms.ModelChoiceField( label=_('Template'), queryset=MessageTemplate.objects.none(), required=False, widget=forms.Select(attrs={ 'class': 'form-select select2', 'id': 'templateSelect' }) ) subject = forms.CharField( label=_('Subject'), required=False, widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': _('Email subject (for email channel only)') }) ) message = forms.CharField( label=_('Message'), required=False, widget=forms.Textarea(attrs={ 'class': 'form-control', 'rows': 6, 'placeholder': _('Enter your message here...') }) ) recipient_filter = forms.ChoiceField( label=_('Recipients'), choices=[ ('all', _('All Patients')), ('tags', _('By Tags')), ('custom', _('Custom List')), ], widget=forms.RadioSelect(attrs={ 'class': 'form-check-input' }) ) tags = forms.CharField( label=_('Patient Tags'), required=False, widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': _('Enter tags separated by commas') }) ) recipients = forms.CharField( label=_('Recipients'), required=False, widget=forms.Textarea(attrs={ 'class': 'form-control', 'rows': 4, 'placeholder': _('Enter phone numbers or emails, one per line') }) ) def __init__(self, *args, tenant=None, **kwargs): super().__init__(*args, **kwargs) if tenant: self.fields['template'].queryset = MessageTemplate.objects.filter( tenant=tenant, is_active=True ).order_by('name') def clean(self): cleaned_data = super().clean() use_template = cleaned_data.get('use_template') template = cleaned_data.get('template') message = cleaned_data.get('message') recipient_filter = cleaned_data.get('recipient_filter') tags = cleaned_data.get('tags') recipients = cleaned_data.get('recipients') # Validate message content if use_template and not template: raise ValidationError(_('Please select a template')) if not use_template and not message: raise ValidationError(_('Please enter a message')) # Validate recipients if recipient_filter == 'tags' and not tags: raise ValidationError(_('Please enter at least one tag')) if recipient_filter == 'custom' and not recipients: raise ValidationError(_('Please enter at least one recipient')) return cleaned_data class TestTemplateForm(forms.Form): """Form for testing message templates.""" template = forms.ModelChoiceField( label=_('Template'), queryset=MessageTemplate.objects.none(), widget=forms.Select(attrs={ 'class': 'form-select select2' }) ) test_recipient = forms.CharField( label=_('Test Recipient'), widget=forms.TextInput(attrs={ 'class': 'form-control', 'placeholder': _('Phone number or email') }) ) language = forms.ChoiceField( label=_('Language'), choices=[('en', _('English')), ('ar', _('Arabic'))], widget=forms.Select(attrs={ 'class': 'form-select' }) ) variables = forms.CharField( label=_('Variables (JSON)'), required=False, widget=forms.Textarea(attrs={ 'class': 'form-control', 'rows': 4, 'placeholder': _('{"patient_name": "Ahmed", "appointment_date": "2025-10-15"}') }) ) def __init__(self, *args, tenant=None, **kwargs): super().__init__(*args, **kwargs) if tenant: self.fields['template'].queryset = MessageTemplate.objects.filter( tenant=tenant, is_active=True ).order_by('name') def clean_variables(self): """Validate and parse variables JSON.""" import json variables = self.cleaned_data.get('variables') if not variables: return {} try: variables = json.loads(variables) except json.JSONDecodeError: raise ValidationError(_('Invalid JSON format')) if not isinstance(variables, dict): raise ValidationError(_('Variables must be a JSON object')) return variables class MessageRetryForm(forms.Form): """Form for retrying failed messages.""" message_ids = forms.CharField( widget=forms.HiddenInput() ) def clean_message_ids(self): """Parse and validate message IDs.""" import json message_ids = self.cleaned_data.get('message_ids') try: message_ids = json.loads(message_ids) except json.JSONDecodeError: raise ValidationError(_('Invalid message IDs')) if not isinstance(message_ids, list): raise ValidationError(_('Message IDs must be a list')) return message_ids