Add shopper accounts, reviews, tracking, and seed_demo (#9)

Closes #9. Shop-gated buyer accounts, purchase reviews, Stripe customer ids, shipment tracking, slim public contact form, and a template-neutral seed_demo command.
This commit is contained in:
2026-09-07 08:35:55 -05:00
parent 9cdce7a897
commit 5b11cc18c7
66 changed files with 3051 additions and 285 deletions
-79
View File
@@ -10,88 +10,9 @@ class ContactForm(forms.Form):
("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"}),