from __future__ import annotations import logging from django import forms from django.contrib.auth import authenticate, get_user_model from django.contrib.auth.forms import AuthenticationForm, PasswordResetForm, SetPasswordForm, UserCreationForm from django.utils import timezone from .models import Business, BusinessMembership, Feedback, ProofCard, ReviewRequest logger = logging.getLogger(__name__) User = get_user_model() class TrustForgeAuthenticationForm(AuthenticationForm): username = forms.CharField( label='Work email', widget=forms.EmailInput( attrs={ 'class': 'form-control form-control-lg', 'placeholder': 'you@company.com', 'autocomplete': 'email', } ), ) password = forms.CharField( label='Password', strip=False, widget=forms.PasswordInput( attrs={ 'class': 'form-control form-control-lg', 'placeholder': 'Enter your password', 'autocomplete': 'current-password', } ), ) def clean(self): email = (self.cleaned_data.get('username') or '').strip().lower() password = self.cleaned_data.get('password') if email and password: matched_user = User._default_manager.filter(email__iexact=email).first() auth_username = matched_user.get_username() if matched_user else email self.user_cache = authenticate(self.request, username=auth_username, password=password) if self.user_cache is None: raise self.get_invalid_login_error() self.confirm_login_allowed(self.user_cache) return self.cleaned_data class SignUpForm(UserCreationForm): first_name = forms.CharField( required=False, max_length=150, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Avery'}), ) last_name = forms.CharField( required=False, max_length=150, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Stone'}), ) email = forms.EmailField( widget=forms.EmailInput( attrs={ 'class': 'form-control form-control-lg', 'placeholder': 'owner@servicebrand.com', 'autocomplete': 'email', } ) ) password1 = forms.CharField( label='Password', strip=False, widget=forms.PasswordInput( attrs={ 'class': 'form-control form-control-lg', 'placeholder': 'Create a password', 'autocomplete': 'new-password', } ), ) password2 = forms.CharField( label='Confirm password', strip=False, widget=forms.PasswordInput( attrs={ 'class': 'form-control form-control-lg', 'placeholder': 'Repeat your password', 'autocomplete': 'new-password', } ), ) class Meta(UserCreationForm.Meta): model = User fields = ('first_name', 'last_name', 'email', 'password1', 'password2') def clean_email(self): email = (self.cleaned_data.get('email') or '').strip().lower() if User._default_manager.filter(email__iexact=email).exists() or User._default_manager.filter(username__iexact=email).exists(): raise forms.ValidationError('An account with this email already exists.') return email def save(self, commit=True): user = super().save(commit=False) email = self.cleaned_data['email'] user.username = email user.email = email user.first_name = self.cleaned_data.get('first_name', '').strip() user.last_name = self.cleaned_data.get('last_name', '').strip() if commit: user.save() return user class ProfileSettingsForm(forms.ModelForm): first_name = forms.CharField( required=False, max_length=150, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Avery'}), ) last_name = forms.CharField( required=False, max_length=150, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Stone'}), ) email = forms.EmailField( widget=forms.EmailInput( attrs={ 'class': 'form-control form-control-lg', 'placeholder': 'you@company.com', 'autocomplete': 'email', } ) ) class Meta: model = User fields = ('first_name', 'last_name', 'email') def clean_email(self): email = (self.cleaned_data.get('email') or '').strip().lower() email_exists = User._default_manager.filter(email__iexact=email).exclude(pk=self.instance.pk).exists() username_exists = User._default_manager.filter(username__iexact=email).exclude(pk=self.instance.pk).exists() if email_exists or username_exists: raise forms.ValidationError('Another account already uses this email.') return email def save(self, commit=True): user = super().save(commit=False) email = self.cleaned_data['email'] user.email = email user.username = email user.first_name = self.cleaned_data.get('first_name', '').strip() user.last_name = self.cleaned_data.get('last_name', '').strip() if commit: user.save() return user class TrustForgePasswordResetForm(PasswordResetForm): email = forms.EmailField( widget=forms.EmailInput( attrs={ 'class': 'form-control form-control-lg', 'placeholder': 'you@company.com', 'autocomplete': 'email', } ) ) def send_mail(self, *args, **kwargs): try: return super().send_mail(*args, **kwargs) except Exception: logger.exception('Password reset email failed to send.') class TrustForgeSetPasswordForm(SetPasswordForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['new_password1'].widget.attrs.update( { 'class': 'form-control form-control-lg', 'placeholder': 'Create a new password', 'autocomplete': 'new-password', } ) self.fields['new_password2'].widget.attrs.update( { 'class': 'form-control form-control-lg', 'placeholder': 'Confirm the new password', 'autocomplete': 'new-password', } ) class BusinessOnboardingForm(forms.ModelForm): class Meta: model = Business fields = ('name', 'industry', 'primary_city', 'primary_state', 'google_review_url') widgets = { 'name': forms.TextInput(attrs={'class': 'form-control form-control-lg', 'placeholder': 'Summit Home Services'}), 'industry': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'HVAC, Roofing, Plumbing, Junk Removal…'}), 'primary_city': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Austin'}), 'primary_state': forms.TextInput(attrs={'class': 'form-control text-uppercase', 'placeholder': 'TX'}), 'google_review_url': forms.URLInput(attrs={'class': 'form-control', 'placeholder': 'https://g.page/r/.../review'}), } def clean_primary_state(self): return (self.cleaned_data.get('primary_state') or '').upper() class BusinessSettingsForm(BusinessOnboardingForm): pass class TeamMemberInviteForm(forms.Form): first_name = forms.CharField( required=False, max_length=150, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Jamie'}), ) last_name = forms.CharField( required=False, max_length=150, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Rivera'}), ) email = forms.EmailField( widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'tech@servicebrand.com'}) ) role = forms.ChoiceField( choices=BusinessMembership.Role.choices, initial=BusinessMembership.Role.TECHNICIAN, widget=forms.Select(attrs={'class': 'form-select'}), ) def clean_email(self): return (self.cleaned_data.get('email') or '').strip().lower() class JobIntakeForm(forms.Form): business = forms.ModelChoiceField( queryset=Business.objects.filter(is_active=True), empty_label=None, widget=forms.Select(attrs={'class': 'form-select form-control-lg'}), ) customer_name = forms.CharField( max_length=160, widget=forms.TextInput(attrs={'class': 'form-control form-control-lg', 'placeholder': 'Homeowner or business contact'}), ) customer_email = forms.EmailField( required=False, widget=forms.EmailInput(attrs={'class': 'form-control', 'placeholder': 'customer@example.com'}), ) customer_phone = forms.CharField( required=False, max_length=40, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': '(555) 555-0123'}), ) service_type = forms.CharField( max_length=120, widget=forms.TextInput(attrs={'class': 'form-control form-control-lg', 'placeholder': 'Roof replacement, HVAC tune-up, junk removal…'}), ) description = forms.CharField( required=False, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3, 'placeholder': 'What did the crew complete on-site?'}), ) customer_city = forms.CharField( max_length=120, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Austin'}), ) customer_state = forms.CharField( max_length=2, widget=forms.TextInput(attrs={'class': 'form-control text-uppercase', 'placeholder': 'TX'}), ) technician_name = forms.CharField( required=False, max_length=120, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Luis R.'}), ) completion_date = forms.DateField( initial=timezone.localdate, widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}), ) project_value = forms.DecimalField( required=False, min_value=0, max_digits=10, decimal_places=2, widget=forms.NumberInput(attrs={'class': 'form-control', 'placeholder': '4500'}), ) before_photo = forms.FileField( required=False, widget=forms.ClearableFileInput(attrs={'class': 'form-control', 'accept': 'image/*'}), ) after_photo = forms.FileField( required=False, widget=forms.ClearableFileInput(attrs={'class': 'form-control', 'accept': 'image/*'}), ) anonymize_customer = forms.BooleanField( required=False, initial=True, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}), ) send_review_request = forms.BooleanField( required=False, initial=True, widget=forms.CheckboxInput(attrs={'class': 'form-check-input'}), ) review_channel = forms.ChoiceField( choices=ReviewRequest.Channel.choices, initial=ReviewRequest.Channel.EMAIL, widget=forms.Select(attrs={'class': 'form-select'}), ) def __init__(self, *args, business: Business | None = None, **kwargs): super().__init__(*args, **kwargs) if business is not None: self.fields['business'].queryset = Business.objects.filter(pk=business.pk) self.fields['business'].initial = business self.fields['business'].widget = forms.HiddenInput() def clean_customer_state(self): return self.cleaned_data['customer_state'].upper() class PublicFeedbackForm(forms.Form): experience = forms.ChoiceField( choices=Feedback.Experience.choices, widget=forms.RadioSelect, ) testimonial = forms.CharField( required=False, widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 4, 'placeholder': 'Tell us what stood out about the service…'}), ) def clean(self): cleaned_data = super().clean() experience = cleaned_data.get('experience') testimonial = (cleaned_data.get('testimonial') or '').strip() if experience in {Feedback.Experience.GREAT, Feedback.Experience.GOOD} and len(testimonial) < 12: self.add_error('testimonial', 'For positive feedback, add a short testimonial so it can power the proof card.') return cleaned_data class ProofCardForm(forms.ModelForm): class Meta: model = ProofCard fields = [ 'customer_display_name', 'is_anonymized', 'testimonial_quote', 'rating', 'status', 'is_featured', 'attached_widget_label', 'attached_pages', ] widgets = { 'customer_display_name': forms.TextInput(attrs={'class': 'form-control'}), 'is_anonymized': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'testimonial_quote': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}), 'rating': forms.NumberInput(attrs={'class': 'form-control', 'min': 1, 'max': 5}), 'status': forms.Select(attrs={'class': 'form-select'}), 'is_featured': forms.CheckboxInput(attrs={'class': 'form-check-input'}), 'attached_widget_label': forms.TextInput(attrs={'class': 'form-control'}), 'attached_pages': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), }