generated from westfarn/web_django_template
## Summary - Rebrand the Django client template as Print Forge (`client_site` → `print_forge`) with shop + shipping enabled. - Product listings support multiple photos, color variants (shared price/description/STL, per-color stock and photos), and a photo-first / 3D-second gallery. - Public pages use 3D printer / printed-toy photography instead of leftover t-shirt mockups; beta CI deploys on `master`. Closes #1 Infra: [server-infra#27](ai_ml_operations/server-infra#27) (easy deploy beta). ## Test plan - [ ] Product page shows photo first, 3D model second; color swatches swap photos and stock - [ ] Portal can upload multiple photos and per-color qty/images; STL stays shared - [ ] Public home/about/gallery have no t-shirt mockups - [ ] `manage.py test` passes - [ ] After server-infra#27: beta deploy to `print-forge-preview.aimloperations.com` Reviewed-on: #2
117 lines
3.4 KiB
Python
117 lines
3.4 KiB
Python
from django import forms
|
|
from django.conf import settings
|
|
|
|
|
|
class ContactForm(forms.Form):
|
|
INTEREST_CHOICES = [
|
|
("general", "General inquiry"),
|
|
("custom", "Custom toy / print"),
|
|
("order", "Order question"),
|
|
("wholesale", "Wholesale"),
|
|
("other", "Something else"),
|
|
]
|
|
|
|
first_name = forms.CharField(
|
|
max_length=100,
|
|
widget=forms.TextInput(attrs={"class": "form-input", "id": "contact-first-name"}),
|
|
)
|
|
last_name = forms.CharField(
|
|
max_length=100,
|
|
required=False,
|
|
widget=forms.TextInput(attrs={"class": "form-input", "id": "contact-last-name"}),
|
|
)
|
|
email = forms.EmailField(
|
|
widget=forms.EmailInput(attrs={"class": "form-input", "id": "contact-email"}),
|
|
)
|
|
phone = forms.CharField(
|
|
max_length=32,
|
|
required=False,
|
|
widget=forms.TextInput(attrs={"class": "form-input", "id": "contact-phone"}),
|
|
)
|
|
address_line1 = forms.CharField(
|
|
max_length=200,
|
|
required=False,
|
|
label="Street address",
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-input",
|
|
"id": "contact-address-line1",
|
|
"autocomplete": "off",
|
|
"data-ac": "line1",
|
|
}
|
|
),
|
|
)
|
|
address_line2 = forms.CharField(
|
|
max_length=200,
|
|
required=False,
|
|
label="Apt / suite",
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-input",
|
|
"id": "contact-address-line2",
|
|
"autocomplete": "address-line2",
|
|
"data-ac": "line2",
|
|
}
|
|
),
|
|
)
|
|
address_city = forms.CharField(
|
|
max_length=100,
|
|
required=False,
|
|
label="City",
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-input",
|
|
"id": "contact-address-city",
|
|
"autocomplete": "address-level2",
|
|
"data-ac": "city",
|
|
}
|
|
),
|
|
)
|
|
address_state = forms.CharField(
|
|
max_length=32,
|
|
required=False,
|
|
label="State",
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-input",
|
|
"id": "contact-address-state",
|
|
"autocomplete": "address-level1",
|
|
"data-ac": "state",
|
|
}
|
|
),
|
|
)
|
|
address_zip = forms.CharField(
|
|
max_length=20,
|
|
required=False,
|
|
label="ZIP",
|
|
widget=forms.TextInput(
|
|
attrs={
|
|
"class": "form-input",
|
|
"id": "contact-address-zip",
|
|
"autocomplete": "postal-code",
|
|
"data-ac": "zip",
|
|
}
|
|
),
|
|
)
|
|
interest = forms.ChoiceField(
|
|
choices=INTEREST_CHOICES,
|
|
widget=forms.Select(attrs={"class": "form-input", "id": "contact-interest"}),
|
|
)
|
|
message = forms.CharField(
|
|
widget=forms.Textarea(attrs={"class": "form-input", "id": "contact-message"}),
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
if settings.RECAPTCHA_PUBLIC_KEY and settings.RECAPTCHA_PRIVATE_KEY:
|
|
from django_recaptcha.fields import ReCaptchaField
|
|
from django_recaptcha.widgets import ReCaptchaV3
|
|
|
|
self.fields["captcha"] = ReCaptchaField(widget=ReCaptchaV3)
|
|
|
|
|
|
class NotifyForm(forms.Form):
|
|
email = forms.EmailField(
|
|
widget=forms.EmailInput(attrs={"class": "form-input"}),
|
|
)
|