from __future__ import annotations from django import forms from django.contrib.auth import get_user_model from django.core.exceptions import ValidationError from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Field from django.contrib.auth import get_user_model from orgs.models import UserProfile class MultiFileInput(forms.ClearableFileInput): allow_multiple_selected = True class MultiFileField(forms.Field): widget = MultiFileInput def __init__(self, *args, **kwargs): kwargs.setdefault("required", False) super().__init__(*args, **kwargs) def to_python(self, data): # Accept a list of UploadedFile or an empty list/None return data def validate(self, value): # Only enforce presence if required=True if self.required and not value: raise forms.ValidationError("This field is required.") class PickupRequestForm(forms.Form): name = forms.CharField(max_length=255) email = forms.EmailField(required=False) phone = forms.CharField(max_length=64, required=False) address = forms.CharField(widget=forms.Textarea) preferred_at = forms.DateTimeField(required=False, widget=forms.DateTimeInput(attrs={"type": "datetime-local"})) materials = forms.CharField(label="Materials/Notes", widget=forms.Textarea, required=False) photos = MultiFileField(widget=MultiFileInput, help_text="Optional: upload photos of scrap") class ContactForm(forms.Form): name = forms.CharField(max_length=255) email = forms.EmailField() phone = forms.CharField(max_length=64, required=False) subject = forms.CharField(max_length=255, required=False) message = forms.CharField(widget=forms.Textarea) class RegistrationForm(forms.Form): username = forms.CharField(max_length=150) email = forms.EmailField() password1 = forms.CharField(widget=forms.PasswordInput) password2 = forms.CharField(widget=forms.PasswordInput) role = forms.ChoiceField(choices=[(k, v) for k, v in UserProfile.ROLE_CHOICES if k != UserProfile.ROLE_OWNER]) def clean_username(self): username = self.cleaned_data["username"].strip() User = get_user_model() if User.objects.filter(username__iexact=username).exists(): raise forms.ValidationError("Username already taken.") return username def clean_email(self): email = self.cleaned_data["email"].strip() User = get_user_model() if User.objects.filter(email__iexact=email).exists(): raise forms.ValidationError("Email already registered.") return email def clean(self): data = super().clean() p1 = data.get("password1") p2 = data.get("password2") if p1 and p2 and p1 != p2: self.add_error("password2", "Passwords do not match.") return data class PublicUserForm(forms.ModelForm): class Meta: model = get_user_model() fields = ["first_name", "last_name", "email"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_tag = False self.helper.layout = Layout( Field("first_name"), Field("last_name"), Field("email"), ) from api.models import Profile as ApiProfile from orgs.models import UserProfile class PublicProfileForm(forms.ModelForm): tags = forms.CharField(label="Tags", required=False, help_text="Comma-separated") class Meta: model = ApiProfile fields = ["bio", "interests", "industry"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_tag = False self.helper.layout = Layout( Field("bio"), Field("interests"), Field("industry"), Field("tags"), ) if self.instance and getattr(self.instance, "pk", None): try: names = list(self.instance.tags.names()) self.fields["tags"].initial = ", ".join(names) except Exception: self.fields["tags"].initial = "" def save(self, commit: bool = True): profile = super().save(commit) tags = [t.strip() for t in self.cleaned_data.get("tags", "").split(",") if t.strip()] try: if commit and hasattr(profile, "tags"): profile.tags.set(tags) except Exception: pass return profile class PublicUserPhotoForm(forms.ModelForm): remove_photo = forms.BooleanField(label="Remove current photo", required=False) class Meta: model = UserProfile fields = ["my_photo", "remove_photo"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields["my_photo"].widget = forms.ClearableFileInput(attrs={ "accept": "image/*", "class": "hidden", "id": "id_my_photo", }) self.helper = FormHelper() self.helper.form_tag = False self.helper.layout = Layout( Field("my_photo"), Field("remove_photo"), ) def clean_my_photo(self): f = self.cleaned_data.get("my_photo") if not f: return f try: content_type = getattr(f, "content_type", "") if content_type and not content_type.startswith("image/"): raise ValidationError("Please upload an image file.") except Exception: pass max_bytes = 5 * 1024 * 1024 if getattr(f, "size", 0) and f.size > max_bytes: raise ValidationError("Image too large (max 5MB).") return f def save(self, commit: bool = True): instance: UserProfile = super().save(commit=False) remove = self.cleaned_data.get("remove_photo", False) new_file = self.cleaned_data.get("my_photo") if remove and not new_file: try: if instance.my_photo: instance.my_photo.delete(save=False) except Exception: pass instance.my_photo = None if commit: instance.save() return instance