39546-vm/core/forms.py
Flatlogic Bot 159e91248c 1
2026-04-11 01:49:55 +00:00

373 lines
14 KiB
Python

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}),
}