redesign proto
This commit is contained in:
@@ -1,4 +1,13 @@
|
||||
stripe
|
||||
django
|
||||
django-adminplus
|
||||
django-recaptcha
|
||||
asgiref==3.8.1
|
||||
certifi==2025.1.31
|
||||
charset-normalizer==3.4.1
|
||||
Django==5.2
|
||||
django-phonenumber-field==8.1.0
|
||||
django-recaptcha==4.1.0
|
||||
idna==3.10
|
||||
phonenumbers==9.0.3
|
||||
requests==2.32.3
|
||||
sqlparse==0.5.3
|
||||
stripe==12.0.0
|
||||
typing_extensions==4.13.2
|
||||
urllib3==2.4.0
|
||||
|
||||
@@ -11,6 +11,6 @@ import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'scha.settings')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "scha.settings")
|
||||
|
||||
application = get_asgi_application()
|
||||
|
||||
@@ -20,7 +20,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-0&6)5#t(wghsn$q7pg#s!x+_!s_y#labf6dh*8q%34q25iuag*'
|
||||
SECRET_KEY = "django-insecure-0&6)5#t(wghsn$q7pg#s!x+_!s_y#labf6dh*8q%34q25iuag*"
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
@@ -31,55 +31,55 @@ ALLOWED_HOSTS = []
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'schasite.apps.SchasiteConfig',
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'phonenumber_field',
|
||||
'django_recaptcha',
|
||||
"schasite.apps.SchasiteConfig",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"phonenumber_field",
|
||||
"django_recaptcha",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'scha.urls'
|
||||
ROOT_URLCONF = "scha.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'scha.wsgi.application'
|
||||
WSGI_APPLICATION = "scha.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.sqlite3",
|
||||
"NAME": BASE_DIR / "db.sqlite3",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,16 +89,16 @@ DATABASES = {
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -106,9 +106,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
TIME_ZONE = "UTC"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
@@ -118,16 +118,18 @@ USE_TZ = True
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
STATIC_URL = "static/"
|
||||
|
||||
# Default primary key field type
|
||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
STRIPE_PUBLISHABLE_KEY = 'pk_test_51OxENZDV0RPXOyxGkZnncLFbpVoQq6qxUSN08BPDTpHjrm2UTtvOrBG2A2IQhX3oxdYd6amGaci00SKchYZ5DUUN00jTBzhh62'
|
||||
STRIPE_SECRET_KEY = 'sk_test_51OxENZDV0RPXOyxGkULVr0MvVvXgNWsKjWiP54a9ls02LgsgXnzEwP0IQ0VjvP4Wt4RrPx1FvzHSbLvChQGOKvv800Ui0NBvIA'
|
||||
STRIPE_ENDPOINT_SECRET = 'whsec_a38b2d115b64d86713397e2718e2d42e5d263139dfbac6c0badc6f9ca8f72557'
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
STRIPE_PUBLISHABLE_KEY = "pk_test_51OxENZDV0RPXOyxGkZnncLFbpVoQq6qxUSN08BPDTpHjrm2UTtvOrBG2A2IQhX3oxdYd6amGaci00SKchYZ5DUUN00jTBzhh62"
|
||||
STRIPE_SECRET_KEY = "sk_test_51OxENZDV0RPXOyxGkULVr0MvVvXgNWsKjWiP54a9ls02LgsgXnzEwP0IQ0VjvP4Wt4RrPx1FvzHSbLvChQGOKvv800Ui0NBvIA"
|
||||
STRIPE_ENDPOINT_SECRET = (
|
||||
"whsec_a38b2d115b64d86713397e2718e2d42e5d263139dfbac6c0badc6f9ca8f72557"
|
||||
)
|
||||
|
||||
# Recaptcha Stuff
|
||||
RECAPTCHA_PUBLIC_KEY = '6LesfrkpAAAAAGJ5vYp4KuuGcyfk70HkihCwp3d3'
|
||||
RECAPTCHA_PRIVATE_KEY = '6LesfrkpAAAAABCcDGli5cZiq1hfS0lfRiXg0mlI'
|
||||
RECAPTCHA_PUBLIC_KEY = "6LesfrkpAAAAAGJ5vYp4KuuGcyfk70HkihCwp3d3"
|
||||
RECAPTCHA_PRIVATE_KEY = "6LesfrkpAAAAABCcDGli5cZiq1hfS0lfRiXg0mlI"
|
||||
|
||||
@@ -14,6 +14,7 @@ Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
from django.conf.urls.static import static
|
||||
@@ -23,8 +24,8 @@ from schasite.views import stripe_cancelled, stripe_success, stripe_webhook
|
||||
|
||||
urlpatterns = [
|
||||
path("schasite/", include("schasite.urls")),
|
||||
path('success/', stripe_success),
|
||||
path('cancelled/', stripe_cancelled),
|
||||
path('webhook/', stripe_webhook),
|
||||
path('admin/', admin.site.urls),
|
||||
path("success/", stripe_success),
|
||||
path("cancelled/", stripe_cancelled),
|
||||
path("webhook/", stripe_webhook),
|
||||
path("admin/", admin.site.urls),
|
||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
|
||||
@@ -11,6 +11,6 @@ import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'scha.settings')
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "scha.settings")
|
||||
|
||||
application = get_wsgi_application()
|
||||
|
||||
@@ -1,192 +1,250 @@
|
||||
from django.contrib import admin
|
||||
from .models import UsefulLinks, Membership,CalendarEvent, MembershipServices, AddressModel1, MembershipPerson, MembershipCommittee, CalendarEventAddressModel, Payments
|
||||
from .models import (
|
||||
UsefulLinks,
|
||||
Membership,
|
||||
CalendarEvent,
|
||||
MembershipServices,
|
||||
AddressModel1,
|
||||
MembershipPerson,
|
||||
MembershipCommittee,
|
||||
CalendarEventAddressModel,
|
||||
Payments,
|
||||
SCHAOfficer,
|
||||
)
|
||||
from django.http import HttpResponse
|
||||
from datetime import datetime
|
||||
from .forms import PaymentImport
|
||||
|
||||
# Register your models here.
|
||||
|
||||
|
||||
class UsefulLinksAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "url"]
|
||||
|
||||
|
||||
class MembershipAddressInline(admin.TabularInline):
|
||||
model = AddressModel1
|
||||
extra = 1
|
||||
readonly_fields = ('id',)
|
||||
readonly_fields = ("id",)
|
||||
|
||||
|
||||
class MembershipPersonInline(admin.TabularInline):
|
||||
model = MembershipPerson
|
||||
extra = 1
|
||||
readonly_fields = ('id',)
|
||||
readonly_fields = ("id",)
|
||||
|
||||
|
||||
class MembershipCommiteeInline(admin.TabularInline):
|
||||
model = MembershipCommittee
|
||||
extra = 1
|
||||
readonly_fields = ('id',)
|
||||
readonly_fields = ("id",)
|
||||
|
||||
|
||||
class MembershipServicesInline(admin.TabularInline):
|
||||
model = MembershipServices
|
||||
extra = 1
|
||||
readonly_fields = ('id',)
|
||||
readonly_fields = ("id",)
|
||||
|
||||
|
||||
def download_csv_by_members(modelAdmin, request, queryset):
|
||||
import csv
|
||||
import csv
|
||||
import io as StringIO
|
||||
|
||||
def stream_csv(queryset):
|
||||
csvfile = StringIO.StringIO()
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow([
|
||||
'address_1',
|
||||
'city',
|
||||
'state',
|
||||
'zip_code',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email',
|
||||
'phone_number'
|
||||
])
|
||||
writer.writerow(
|
||||
[
|
||||
"address_1",
|
||||
"city",
|
||||
"state",
|
||||
"zip_code",
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"phone_number",
|
||||
]
|
||||
)
|
||||
for q in queryset:
|
||||
people = [item for item in MembershipPerson.objects.filter(membership_id=q.id)]
|
||||
people = [
|
||||
item for item in MembershipPerson.objects.filter(membership_id=q.id)
|
||||
]
|
||||
for person in people:
|
||||
writer.writerow([
|
||||
q.addressmodel1.address_1,
|
||||
q.addressmodel1.city,
|
||||
q.addressmodel1.state,
|
||||
q.addressmodel1.zip_code,
|
||||
person.first_name,
|
||||
person.last_name,
|
||||
person.email,
|
||||
person.phone_number
|
||||
])
|
||||
writer.writerow(
|
||||
[
|
||||
q.addressmodel1.address_1,
|
||||
q.addressmodel1.city,
|
||||
q.addressmodel1.state,
|
||||
q.addressmodel1.zip_code,
|
||||
person.first_name,
|
||||
person.last_name,
|
||||
person.email,
|
||||
person.phone_number,
|
||||
]
|
||||
)
|
||||
yield csvfile.getvalue()
|
||||
|
||||
|
||||
now = datetime.now()
|
||||
filename = now.strftime("%Y_%m_%d_%H_%M_%S") + "_scha_member_by_member.csv"
|
||||
response = HttpResponse(stream_csv(queryset), content_type="text/csv")
|
||||
response['Content-Disposition'] = "attachment; filename={}".format(filename)
|
||||
response["Content-Disposition"] = "attachment; filename={}".format(filename)
|
||||
return response
|
||||
|
||||
|
||||
|
||||
def download_csv_by_address(modeladmin, request, queryset):
|
||||
import csv
|
||||
import io as StringIO
|
||||
|
||||
def stream_csv(queryset):
|
||||
csvfile = StringIO.StringIO()
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow(['address_1',
|
||||
"city",
|
||||
"state",
|
||||
"zip_code",
|
||||
"person_1_email",
|
||||
"person_1_phone",
|
||||
"person_1_first_name",
|
||||
"person_1_last_name",
|
||||
"person_2_email",
|
||||
"person_2_phone",
|
||||
"person_2_first_name",
|
||||
"person_2_last_name"])
|
||||
|
||||
writer.writerow(
|
||||
[
|
||||
"address_1",
|
||||
"city",
|
||||
"state",
|
||||
"zip_code",
|
||||
"person_1_email",
|
||||
"person_1_phone",
|
||||
"person_1_first_name",
|
||||
"person_1_last_name",
|
||||
"person_2_email",
|
||||
"person_2_phone",
|
||||
"person_2_first_name",
|
||||
"person_2_last_name",
|
||||
]
|
||||
)
|
||||
|
||||
for q in queryset:
|
||||
people = [item for item in MembershipPerson.objects.filter(membership_id=q.id)]
|
||||
writer.writerow([
|
||||
q.addressmodel1.address_1,
|
||||
q.addressmodel1.city,
|
||||
q.addressmodel1.state,
|
||||
q.addressmodel1.zip_code,
|
||||
people[0].email if len(people) > 0 else "",
|
||||
people[0].phone_number if len(people) > 0 else "",
|
||||
people[0].first_name if len(people) > 0 else "",
|
||||
people[0].last_name if len(people) > 0 else "",
|
||||
people[1].email if len(people) > 1 else "",
|
||||
people[1].phone_number if len(people) > 1 else "",
|
||||
people[1].first_name if len(people) > 1 else "",
|
||||
people[1].last_name if len(people) > 1 else "",
|
||||
])
|
||||
people = [
|
||||
item for item in MembershipPerson.objects.filter(membership_id=q.id)
|
||||
]
|
||||
writer.writerow(
|
||||
[
|
||||
q.addressmodel1.address_1,
|
||||
q.addressmodel1.city,
|
||||
q.addressmodel1.state,
|
||||
q.addressmodel1.zip_code,
|
||||
people[0].email if len(people) > 0 else "",
|
||||
people[0].phone_number if len(people) > 0 else "",
|
||||
people[0].first_name if len(people) > 0 else "",
|
||||
people[0].last_name if len(people) > 0 else "",
|
||||
people[1].email if len(people) > 1 else "",
|
||||
people[1].phone_number if len(people) > 1 else "",
|
||||
people[1].first_name if len(people) > 1 else "",
|
||||
people[1].last_name if len(people) > 1 else "",
|
||||
]
|
||||
)
|
||||
yield csvfile.getvalue()
|
||||
|
||||
now = datetime.now()
|
||||
filename = now.strftime("%Y_%m_%d_%H_%M_%S") + "_scha_member_by_address.csv"
|
||||
response = HttpResponse(stream_csv(queryset), content_type="text/csv")
|
||||
response['Content-Disposition'] = "attachment; filename={}".format(filename)
|
||||
response["Content-Disposition"] = "attachment; filename={}".format(filename)
|
||||
return response
|
||||
|
||||
|
||||
class MembershipAdmin(admin.ModelAdmin):
|
||||
inlines = [MembershipAddressInline, MembershipPersonInline, MembershipCommiteeInline, MembershipServicesInline]
|
||||
actions=[download_csv_by_address, download_csv_by_members]
|
||||
inlines = [
|
||||
MembershipAddressInline,
|
||||
MembershipPersonInline,
|
||||
MembershipCommiteeInline,
|
||||
MembershipServicesInline,
|
||||
]
|
||||
actions = [download_csv_by_address, download_csv_by_members]
|
||||
|
||||
|
||||
class CalendarEventAddressInline(admin.TabularInline):
|
||||
model = CalendarEventAddressModel
|
||||
extra = 1
|
||||
readonly_fields = ('id',)
|
||||
readonly_fields = ("id",)
|
||||
|
||||
|
||||
class CalendarEventAdmin(admin.ModelAdmin):
|
||||
inlines = [CalendarEventAddressInline]
|
||||
list_display = ["event_name", "start_date", "end_date", "coordinator_email", "event_link_name"]
|
||||
list_display = [
|
||||
"event_name",
|
||||
"start_date",
|
||||
"end_date",
|
||||
"coordinator_email",
|
||||
"event_link_name",
|
||||
]
|
||||
|
||||
|
||||
class AddressModelAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class MembershipPersonAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class MembershipCommitteeAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class MembershipServicesAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class CalendarEventAddressModelAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
def download_payments(modelAdmin, request, queryset):
|
||||
import csv
|
||||
import csv
|
||||
import io as StringIO
|
||||
|
||||
def stream_payment_csv(queryset):
|
||||
csvfile = StringIO.StringIO()
|
||||
writer = csv.writer(csvfile)
|
||||
writer.writerow([
|
||||
'email',
|
||||
'date',
|
||||
'status',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'phone_number'
|
||||
])
|
||||
writer.writerow(
|
||||
["email", "date", "status", "first_name", "last_name", "phone_number"]
|
||||
)
|
||||
for q in queryset:
|
||||
first_name=""
|
||||
first_name = ""
|
||||
last_name = ""
|
||||
phone_number = ""
|
||||
if q.person:
|
||||
first_name = q.person.first_name if q.person.first_name else ""
|
||||
last_name = q.person.last_name if q.person.last_name else ""
|
||||
phone_number = q.person.phone_number if q.person.phone_number else ""
|
||||
writer.writerow([
|
||||
q.email,
|
||||
q.date,
|
||||
q.status,
|
||||
first_name,
|
||||
last_name,
|
||||
phone_number,
|
||||
])
|
||||
writer.writerow(
|
||||
[
|
||||
q.email,
|
||||
q.date,
|
||||
q.status,
|
||||
first_name,
|
||||
last_name,
|
||||
phone_number,
|
||||
]
|
||||
)
|
||||
yield csvfile.getvalue()
|
||||
|
||||
|
||||
now = datetime.now()
|
||||
filename = now.strftime("%Y_%m_%d_%H_%M_%S") + "_scha_payments_by_member.csv"
|
||||
response = HttpResponse(stream_payment_csv(queryset), content_type="text/csv")
|
||||
response['Content-Disposition'] = "attachment; filename={}".format(filename)
|
||||
response["Content-Disposition"] = "attachment; filename={}".format(filename)
|
||||
return response
|
||||
|
||||
|
||||
class PaymentsAdmin(admin.ModelAdmin):
|
||||
list_display = ["date", "status", "email"]
|
||||
search_fields = ['email']
|
||||
actions=[download_payments]
|
||||
search_fields = ["email"]
|
||||
actions = [download_payments]
|
||||
form = PaymentImport
|
||||
|
||||
class SCHAOfficerAdmin(admin.ModelAdmin):
|
||||
list_display = ["position", "name", "email"]
|
||||
|
||||
admin.site.register(UsefulLinks, UsefulLinksAdmin)
|
||||
admin.site.register(Membership, MembershipAdmin)
|
||||
admin.site.register(CalendarEvent, CalendarEventAdmin)
|
||||
admin.site.register(AddressModel1, AddressModelAdmin)
|
||||
admin.site.register(AddressModel1, AddressModelAdmin)
|
||||
admin.site.register(MembershipPerson, MembershipPersonAdmin)
|
||||
admin.site.register(MembershipCommittee, MembershipCommitteeAdmin)
|
||||
admin.site.register(MembershipServices, MembershipServicesAdmin)
|
||||
admin.site.register(CalendarEventAddressModel, CalendarEventAddressModelAdmin)
|
||||
admin.site.register(Payments, PaymentsAdmin)
|
||||
admin.site.register(MembershipCommittee, MembershipCommitteeAdmin)
|
||||
admin.site.register(MembershipServices, MembershipServicesAdmin)
|
||||
admin.site.register(CalendarEventAddressModel, CalendarEventAddressModelAdmin)
|
||||
admin.site.register(Payments, PaymentsAdmin)
|
||||
admin.site.register(SCHAOfficer, SCHAOfficerAdmin)
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@ from django.apps import AppConfig
|
||||
|
||||
|
||||
class SchasiteConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'schasite'
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "schasite"
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
from django import forms
|
||||
from django.forms import ModelForm
|
||||
from .models import Membership, AddressModel1, MembershipPerson, MembershipCommittee, MembershipServices
|
||||
from .models import (
|
||||
Membership,
|
||||
AddressModel1,
|
||||
MembershipPerson,
|
||||
MembershipCommittee,
|
||||
MembershipServices,
|
||||
)
|
||||
from phonenumber_field.formfields import PhoneNumberField
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
# from django_recaptcha.fields import ReCaptchaField
|
||||
# from django.conf import settings
|
||||
# from django_recaptcha.widgets import ReCaptchaV3
|
||||
@@ -19,45 +26,81 @@ from django.core.exceptions import ValidationError
|
||||
|
||||
# )
|
||||
|
||||
|
||||
class ChildrenForm(ModelForm):
|
||||
class Meta:
|
||||
model = Membership
|
||||
fields = ["children"]
|
||||
|
||||
|
||||
class AddressForm(ModelForm):
|
||||
class Meta:
|
||||
model = AddressModel1
|
||||
fields = ["address_1","address_2","city","state","zip_code"]
|
||||
fields = ["address_1", "address_2", "city", "state", "zip_code"]
|
||||
|
||||
|
||||
class PeopleForm(ModelForm):
|
||||
phone_number = PhoneNumberField(required=False)
|
||||
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
if cleaned_data.get('phone_number'):
|
||||
if len(cleaned_data.get('first_name')) == 0 or len(cleaned_data.get('last_name')) == 0 or len(cleaned_data.get('email')) == 0 or len(cleaned_data.get('phone_number').raw_input) == 0:
|
||||
raise ValidationError ('A Field is missing')
|
||||
if cleaned_data.get("phone_number"):
|
||||
if (
|
||||
len(cleaned_data.get("first_name")) == 0
|
||||
or len(cleaned_data.get("last_name")) == 0
|
||||
or len(cleaned_data.get("email")) == 0
|
||||
or len(cleaned_data.get("phone_number").raw_input) == 0
|
||||
):
|
||||
raise ValidationError("A Field is missing")
|
||||
else:
|
||||
raise ValidationError('No phone number provided')
|
||||
raise ValidationError("No phone number provided")
|
||||
|
||||
class Meta:
|
||||
model = MembershipPerson
|
||||
fields = ["first_name","last_name","phone_number","email"]
|
||||
fields = ["first_name", "last_name", "phone_number", "email"]
|
||||
|
||||
|
||||
class CommitteeForm(ModelForm):
|
||||
block_captain = forms.BooleanField(label="I can serve as a Block Captain (distribute newsletters & directories)", required=False)
|
||||
coordinator = forms.BooleanField(label="I can serve as a Coordinator, SCHA Board Member Position (oversee & organize block captains)", required=False)
|
||||
block_captain = forms.BooleanField(
|
||||
label="I can serve as a Block Captain (distribute newsletters & directories)",
|
||||
required=False,
|
||||
)
|
||||
coordinator = forms.BooleanField(
|
||||
label="I can serve as a Coordinator, SCHA Board Member Position (oversee & organize block captains)",
|
||||
required=False,
|
||||
)
|
||||
egg_hunt = forms.BooleanField(label="Easter Egg Hunt (late March)", required=False)
|
||||
spring_garage_sale = forms.BooleanField(label="Spring Garage Sale (May/June)", required=False)
|
||||
spring_garage_sale = forms.BooleanField(
|
||||
label="Spring Garage Sale (May/June)", required=False
|
||||
)
|
||||
golf_outing = forms.BooleanField(label="Golf Outing (Summer)", required=False)
|
||||
ice_cream_social = forms.BooleanField(label="Ice Creame Social (August)", required=False)
|
||||
fall_garage_sale = forms.BooleanField(label="Fall Garage Sale (September/October)", required=False)
|
||||
halloween_party = forms.BooleanField(label="Halloween Party (October)", required=False)
|
||||
ice_cream_social = forms.BooleanField(
|
||||
label="Ice Creame Social (August)", required=False
|
||||
)
|
||||
fall_garage_sale = forms.BooleanField(
|
||||
label="Fall Garage Sale (September/October)", required=False
|
||||
)
|
||||
halloween_party = forms.BooleanField(
|
||||
label="Halloween Party (October)", required=False
|
||||
)
|
||||
santa_visit = forms.BooleanField(label="Santa Visits (December)", required=False)
|
||||
website = forms.BooleanField(label="SCHA Newsletter/Website (wite articles, edit, report, etc)", required=False)
|
||||
civic_affair = forms.BooleanField(label="Civic Affairs Journalist (report on school board/city council mtgs)", required=False)
|
||||
phone_directory = forms.BooleanField(label="Annual Phone Directory (Help compile, edit, produce our Annual Neighborhood Directory)", required=False)
|
||||
no_preference = forms.BooleanField(label="No Preference. I want to help in some way . Please email me.", required=False)
|
||||
website = forms.BooleanField(
|
||||
label="SCHA Newsletter/Website (wite articles, edit, report, etc)",
|
||||
required=False,
|
||||
)
|
||||
civic_affair = forms.BooleanField(
|
||||
label="Civic Affairs Journalist (report on school board/city council mtgs)",
|
||||
required=False,
|
||||
)
|
||||
phone_directory = forms.BooleanField(
|
||||
label="Annual Phone Directory (Help compile, edit, produce our Annual Neighborhood Directory)",
|
||||
required=False,
|
||||
)
|
||||
no_preference = forms.BooleanField(
|
||||
label="No Preference. I want to help in some way . Please email me.",
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = MembershipCommittee
|
||||
fields = [
|
||||
@@ -76,6 +119,7 @@ class CommitteeForm(ModelForm):
|
||||
"no_preference",
|
||||
]
|
||||
|
||||
|
||||
class ServicesForm(ModelForm):
|
||||
babysitting = forms.BooleanField(label="Babysitting", required=False)
|
||||
lawn_mowing = forms.BooleanField(label="Lawn Mowing", required=False)
|
||||
@@ -84,6 +128,7 @@ class ServicesForm(ModelForm):
|
||||
petsitting = forms.BooleanField(label="Petsitting", required=False)
|
||||
house_sitting = forms.BooleanField(label="Housesitting", required=False)
|
||||
other = forms.BooleanField(label="Other", required=False)
|
||||
|
||||
class Meta:
|
||||
model = MembershipServices
|
||||
fields = [
|
||||
@@ -95,7 +140,8 @@ class ServicesForm(ModelForm):
|
||||
"house_sitting",
|
||||
"other",
|
||||
"other_desc",
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
class PaymentImport(ModelForm):
|
||||
pass
|
||||
pass
|
||||
|
||||
@@ -9,91 +9,186 @@ class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AddressModel',
|
||||
name="AddressModel",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('address_1', models.CharField(max_length=128)),
|
||||
('address_2', models.CharField(blank=True, max_length=128)),
|
||||
('city', models.CharField(max_length=128)),
|
||||
('state', models.CharField(max_length=2)),
|
||||
('zip_code', models.CharField(max_length=5)),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("address_1", models.CharField(max_length=128)),
|
||||
("address_2", models.CharField(blank=True, max_length=128)),
|
||||
("city", models.CharField(max_length=128)),
|
||||
("state", models.CharField(max_length=2)),
|
||||
("zip_code", models.CharField(max_length=5)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Membership',
|
||||
name="Membership",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('children', models.CharField(default='', max_length=256)),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("children", models.CharField(default="", max_length=256)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UsefulLinks',
|
||||
name="UsefulLinks",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=256)),
|
||||
('url', models.CharField(max_length=256)),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=256)),
|
||||
("url", models.CharField(max_length=256)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MembershipPerson',
|
||||
name="MembershipPerson",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('first_name', models.CharField(blank=True, max_length=256, null=True)),
|
||||
('last_name', models.CharField(blank=True, max_length=256, null=True)),
|
||||
('phone_number', phonenumber_field.modelfields.PhoneNumberField(max_length=128, null=True, region=None, unique=True)),
|
||||
('email', models.EmailField(blank=True, max_length=254, null=True)),
|
||||
('membership', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='schasite.membership')),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("first_name", models.CharField(blank=True, max_length=256, null=True)),
|
||||
("last_name", models.CharField(blank=True, max_length=256, null=True)),
|
||||
(
|
||||
"phone_number",
|
||||
phonenumber_field.modelfields.PhoneNumberField(
|
||||
max_length=128, null=True, region=None, unique=True
|
||||
),
|
||||
),
|
||||
("email", models.EmailField(blank=True, max_length=254, null=True)),
|
||||
(
|
||||
"membership",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="schasite.membership",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MembershipCommittee',
|
||||
name="MembershipCommittee",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('block_captain', models.BooleanField(default=False)),
|
||||
('coordinator', models.BooleanField(default=False)),
|
||||
('egg_hunt', models.BooleanField(default=False)),
|
||||
('spring_garage_sale', models.BooleanField(default=False)),
|
||||
('golf_outing', models.BooleanField(default=False)),
|
||||
('ice_cream_social', models.BooleanField(default=False)),
|
||||
('fall_garage_sale', models.BooleanField(default=False)),
|
||||
('halloween_party', models.BooleanField(default=False)),
|
||||
('santa_visit', models.BooleanField(default=False)),
|
||||
('website', models.BooleanField(default=False)),
|
||||
('civic_affair', models.BooleanField(default=False)),
|
||||
('phone_directory', models.BooleanField(default=False)),
|
||||
('no_preference', models.BooleanField(default=False)),
|
||||
('membership', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='schasite.membership')),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("block_captain", models.BooleanField(default=False)),
|
||||
("coordinator", models.BooleanField(default=False)),
|
||||
("egg_hunt", models.BooleanField(default=False)),
|
||||
("spring_garage_sale", models.BooleanField(default=False)),
|
||||
("golf_outing", models.BooleanField(default=False)),
|
||||
("ice_cream_social", models.BooleanField(default=False)),
|
||||
("fall_garage_sale", models.BooleanField(default=False)),
|
||||
("halloween_party", models.BooleanField(default=False)),
|
||||
("santa_visit", models.BooleanField(default=False)),
|
||||
("website", models.BooleanField(default=False)),
|
||||
("civic_affair", models.BooleanField(default=False)),
|
||||
("phone_directory", models.BooleanField(default=False)),
|
||||
("no_preference", models.BooleanField(default=False)),
|
||||
(
|
||||
"membership",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="schasite.membership",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CalendarEvent',
|
||||
name="CalendarEvent",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('event_name', models.CharField(max_length=256)),
|
||||
('start_date', models.DateField(blank=True, null=True)),
|
||||
('end_date', models.DateField(blank=True, null=True)),
|
||||
('location_name', models.CharField(blank=True, max_length=256, null=True)),
|
||||
('coordinator_email', models.EmailField(blank=True, max_length=256, null=True)),
|
||||
('event_link_name', models.CharField(blank=True, max_length=64, null=True)),
|
||||
('event_url', models.URLField(blank=True, max_length=256, null=True)),
|
||||
('location_address', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='schasite.addressmodel')),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("event_name", models.CharField(max_length=256)),
|
||||
("start_date", models.DateField(blank=True, null=True)),
|
||||
("end_date", models.DateField(blank=True, null=True)),
|
||||
(
|
||||
"location_name",
|
||||
models.CharField(blank=True, max_length=256, null=True),
|
||||
),
|
||||
(
|
||||
"coordinator_email",
|
||||
models.EmailField(blank=True, max_length=256, null=True),
|
||||
),
|
||||
(
|
||||
"event_link_name",
|
||||
models.CharField(blank=True, max_length=64, null=True),
|
||||
),
|
||||
("event_url", models.URLField(blank=True, max_length=256, null=True)),
|
||||
(
|
||||
"location_address",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="schasite.addressmodel",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AddressModel1',
|
||||
name="AddressModel1",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('address_1', models.CharField(max_length=128)),
|
||||
('address_2', models.CharField(blank=True, max_length=128)),
|
||||
('city', models.CharField(max_length=128)),
|
||||
('state', models.CharField(max_length=2)),
|
||||
('zip_code', models.CharField(max_length=5)),
|
||||
('membership', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='schasite.membership')),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("address_1", models.CharField(max_length=128)),
|
||||
("address_2", models.CharField(blank=True, max_length=128)),
|
||||
("city", models.CharField(max_length=128)),
|
||||
("state", models.CharField(max_length=2)),
|
||||
("zip_code", models.CharField(max_length=5)),
|
||||
(
|
||||
"membership",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="schasite.membership",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -6,13 +6,13 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('schasite', '0001_initial'),
|
||||
("schasite", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='membership',
|
||||
name='children',
|
||||
field=models.CharField(blank=True, default='', max_length=256, null=True),
|
||||
model_name="membership",
|
||||
name="children",
|
||||
field=models.CharField(blank=True, default="", max_length=256, null=True),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -6,73 +6,73 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('schasite', '0002_alter_membership_children'),
|
||||
("schasite", "0002_alter_membership_children"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='block_captain',
|
||||
model_name="membershipcommittee",
|
||||
name="block_captain",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='civic_affair',
|
||||
model_name="membershipcommittee",
|
||||
name="civic_affair",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='coordinator',
|
||||
model_name="membershipcommittee",
|
||||
name="coordinator",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='egg_hunt',
|
||||
model_name="membershipcommittee",
|
||||
name="egg_hunt",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='fall_garage_sale',
|
||||
model_name="membershipcommittee",
|
||||
name="fall_garage_sale",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='golf_outing',
|
||||
model_name="membershipcommittee",
|
||||
name="golf_outing",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='halloween_party',
|
||||
model_name="membershipcommittee",
|
||||
name="halloween_party",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='ice_cream_social',
|
||||
model_name="membershipcommittee",
|
||||
name="ice_cream_social",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='no_preference',
|
||||
model_name="membershipcommittee",
|
||||
name="no_preference",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='phone_directory',
|
||||
model_name="membershipcommittee",
|
||||
name="phone_directory",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='santa_visit',
|
||||
model_name="membershipcommittee",
|
||||
name="santa_visit",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='spring_garage_sale',
|
||||
model_name="membershipcommittee",
|
||||
name="spring_garage_sale",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='membershipcommittee',
|
||||
name='website',
|
||||
model_name="membershipcommittee",
|
||||
name="website",
|
||||
field=models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -7,13 +7,15 @@ import phonenumber_field.modelfields
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('schasite', '0003_alter_membershipcommittee_block_captain_and_more'),
|
||||
("schasite", "0003_alter_membershipcommittee_block_captain_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='membershipperson',
|
||||
name='phone_number',
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(max_length=128, null=True, region=None),
|
||||
model_name="membershipperson",
|
||||
name="phone_number",
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(
|
||||
max_length=128, null=True, region=None
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -7,23 +7,58 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('schasite', '0004_alter_membershipperson_phone_number'),
|
||||
("schasite", "0004_alter_membershipperson_phone_number"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MembershipServices',
|
||||
name="MembershipServices",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('babysitting', models.BooleanField(blank=True, default=False, null=True)),
|
||||
('lawn_mowing', models.BooleanField(blank=True, default=False, null=True)),
|
||||
('snow_shoveling', models.BooleanField(blank=True, default=False, null=True)),
|
||||
('leaf_raking', models.BooleanField(blank=True, default=False, null=True)),
|
||||
('petsitting', models.BooleanField(blank=True, default=False, null=True)),
|
||||
('house_sitting', models.BooleanField(blank=True, default=False, null=True)),
|
||||
('other', models.BooleanField(blank=True, default=False, null=True)),
|
||||
('other_desc', models.CharField(blank=True, default='', max_length=256, null=True)),
|
||||
('membership', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='schasite.membership')),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"babysitting",
|
||||
models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
(
|
||||
"lawn_mowing",
|
||||
models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
(
|
||||
"snow_shoveling",
|
||||
models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
(
|
||||
"leaf_raking",
|
||||
models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
(
|
||||
"petsitting",
|
||||
models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
(
|
||||
"house_sitting",
|
||||
models.BooleanField(blank=True, default=False, null=True),
|
||||
),
|
||||
("other", models.BooleanField(blank=True, default=False, null=True)),
|
||||
(
|
||||
"other_desc",
|
||||
models.CharField(blank=True, default="", max_length=256, null=True),
|
||||
),
|
||||
(
|
||||
"membership",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="schasite.membership",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -7,31 +7,41 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('schasite', '0005_membershipservices'),
|
||||
("schasite", "0005_membershipservices"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='CalendarEventAddressModel',
|
||||
name="CalendarEventAddressModel",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('address_1', models.CharField(max_length=128)),
|
||||
('address_2', models.CharField(blank=True, max_length=128)),
|
||||
('city', models.CharField(max_length=128)),
|
||||
('state', models.CharField(max_length=2)),
|
||||
('zip_code', models.CharField(max_length=5)),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("address_1", models.CharField(max_length=128)),
|
||||
("address_2", models.CharField(blank=True, max_length=128)),
|
||||
("city", models.CharField(max_length=128)),
|
||||
("state", models.CharField(max_length=2)),
|
||||
("zip_code", models.CharField(max_length=5)),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='calendarevent',
|
||||
name='location_address',
|
||||
model_name="calendarevent",
|
||||
name="location_address",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='AddressModel',
|
||||
name="AddressModel",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='calendareventaddressmodel',
|
||||
name='calendar_event',
|
||||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='schasite.calendarevent'),
|
||||
model_name="calendareventaddressmodel",
|
||||
name="calendar_event",
|
||||
field=models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="schasite.calendarevent"
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -8,18 +8,39 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('schasite', '0006_calendareventaddressmodel_and_more'),
|
||||
("schasite", "0006_calendareventaddressmodel_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Payments',
|
||||
name="Payments",
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('date', models.DateField(default=datetime.datetime(2024, 3, 27, 17, 40, 28, 908036))),
|
||||
('status', models.CharField(default='Completed', max_length=256)),
|
||||
('email', models.EmailField(blank=True, max_length=254, null=True)),
|
||||
('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='schasite.membershipperson')),
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"date",
|
||||
models.DateField(
|
||||
default=datetime.datetime(2024, 3, 27, 17, 40, 28, 908036)
|
||||
),
|
||||
),
|
||||
("status", models.CharField(default="Completed", max_length=256)),
|
||||
("email", models.EmailField(blank=True, max_length=254, null=True)),
|
||||
(
|
||||
"person",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="schasite.membershipperson",
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
||||
@@ -7,13 +7,17 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('schasite', '0007_payments'),
|
||||
("schasite", "0007_payments"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='payments',
|
||||
name='date',
|
||||
field=models.DateField(default=datetime.datetime(2024, 3, 27, 17, 41, 22, 742260, tzinfo=datetime.timezone.utc)),
|
||||
model_name="payments",
|
||||
name="date",
|
||||
field=models.DateField(
|
||||
default=datetime.datetime(
|
||||
2024, 3, 27, 17, 41, 22, 742260, tzinfo=datetime.timezone.utc
|
||||
)
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
40
schasite/migrations/0009_schaofficer_alter_payments_date.py
Normal file
40
schasite/migrations/0009_schaofficer_alter_payments_date.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# Generated by Django 5.2 on 2025-04-14 15:33
|
||||
|
||||
import datetime
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("schasite", "0008_alter_payments_date"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="SCHAOfficer",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=255)),
|
||||
("position", models.CharField(max_length=255)),
|
||||
("email", models.EmailField(max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="payments",
|
||||
name="date",
|
||||
field=models.DateField(
|
||||
default=datetime.datetime(
|
||||
2025, 4, 14, 15, 33, 4, 942154, tzinfo=datetime.timezone.utc
|
||||
)
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -3,7 +3,7 @@ from phonenumber_field.modelfields import PhoneNumberField
|
||||
import datetime
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class UsefulLinks(models.Model):
|
||||
name = models.CharField(max_length=256)
|
||||
@@ -22,7 +22,10 @@ class Membership(models.Model):
|
||||
return self.addressmodel1.address_1
|
||||
|
||||
def get_person_1(self):
|
||||
emails = [item.email for item in MembershipPerson.objects.filter(membership_id=self.id)]
|
||||
emails = [
|
||||
item.email
|
||||
for item in MembershipPerson.objects.filter(membership_id=self.id)
|
||||
]
|
||||
# remove all the Nones
|
||||
filtered_emails = [i for i in emails if i is not None]
|
||||
if len(filtered_emails) == 0:
|
||||
@@ -33,14 +36,14 @@ class Membership(models.Model):
|
||||
def __str__(self):
|
||||
return self.get_address_str() + " | " + self.get_person_1()
|
||||
|
||||
|
||||
class AddressModel1(models.Model):
|
||||
membership = models.OneToOneField(Membership, on_delete=models.CASCADE)
|
||||
address_1 = models.CharField(max_length=128)
|
||||
address_2 = models.CharField(max_length=128, blank=True)
|
||||
city = models.CharField(max_length=128)
|
||||
state = models.CharField(max_length=2)
|
||||
zip_code = models.CharField(max_length=5)
|
||||
|
||||
zip_code = models.CharField(max_length=5)
|
||||
|
||||
|
||||
class CalendarEvent(models.Model):
|
||||
@@ -51,38 +54,45 @@ class CalendarEvent(models.Model):
|
||||
coordinator_email = models.EmailField(max_length=256, blank=True, null=True)
|
||||
event_link_name = models.CharField(max_length=64, blank=True, null=True)
|
||||
event_url = models.URLField(max_length=256, blank=True, null=True)
|
||||
description= models.CharField(max_length=1024, default="")
|
||||
|
||||
def has_date(self):
|
||||
return not self.start_date is None
|
||||
|
||||
|
||||
def past_event(self):
|
||||
if self.has_date():
|
||||
return self.start_date <= datetime.datetime.now().date()
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def future_event(self):
|
||||
if self.has_date():
|
||||
return self.start_date > datetime.datetime.now().date()
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def no_date(self):
|
||||
return not self.has_date()
|
||||
|
||||
|
||||
class CalendarEventAddressModel(models.Model):
|
||||
calendar_event = models.OneToOneField(CalendarEvent, on_delete=models.CASCADE)
|
||||
address_1 = models.CharField(max_length=128)
|
||||
address_2 = models.CharField(max_length=128, blank=True)
|
||||
city = models.CharField(max_length=128)
|
||||
state = models.CharField(max_length=2)
|
||||
zip_code = models.CharField(max_length=5)
|
||||
zip_code = models.CharField(max_length=5)
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
self.address_1 + ", " + self.city + ", " + self.state + " " + self.zip_code
|
||||
)
|
||||
|
||||
|
||||
def __str__ (self):
|
||||
return self.address_1 + ", " + self.city + ", " + self.state + " " + self.zip_code
|
||||
|
||||
class MembershipPerson(models.Model):
|
||||
membership = models.ForeignKey(Membership, on_delete=models.CASCADE, blank=True, null=True)
|
||||
membership = models.ForeignKey(
|
||||
Membership, on_delete=models.CASCADE, blank=True, null=True
|
||||
)
|
||||
first_name = models.CharField(max_length=256, blank=True, null=True)
|
||||
last_name = models.CharField(max_length=256, blank=True, null=True)
|
||||
phone_number = PhoneNumberField(blank=False, null=True)
|
||||
@@ -92,8 +102,6 @@ class MembershipPerson(models.Model):
|
||||
return self.email if self.email else "No email"
|
||||
|
||||
|
||||
|
||||
|
||||
class MembershipCommittee(models.Model):
|
||||
membership = models.OneToOneField(Membership, on_delete=models.CASCADE)
|
||||
block_captain = models.BooleanField(default=False, blank=True, null=True)
|
||||
@@ -110,6 +118,7 @@ class MembershipCommittee(models.Model):
|
||||
phone_directory = models.BooleanField(default=False, blank=True, null=True)
|
||||
no_preference = models.BooleanField(default=False, blank=True, null=True)
|
||||
|
||||
|
||||
class MembershipServices(models.Model):
|
||||
membership = models.OneToOneField(Membership, on_delete=models.CASCADE)
|
||||
babysitting = models.BooleanField(default=False, blank=True, null=True)
|
||||
@@ -121,8 +130,36 @@ class MembershipServices(models.Model):
|
||||
other = models.BooleanField(default=False, blank=True, null=True)
|
||||
other_desc = models.CharField(default="", blank=True, null=True, max_length=256)
|
||||
|
||||
|
||||
class Payments(models.Model):
|
||||
date = models.DateField(default=timezone.now())
|
||||
status = models.CharField(default='Completed', max_length=256)
|
||||
status = models.CharField(default="Completed", max_length=256)
|
||||
email = models.EmailField(blank=True, null=True)
|
||||
person = models.ForeignKey(MembershipPerson, on_delete=models.CASCADE, blank=True,null=True)
|
||||
person = models.ForeignKey(
|
||||
MembershipPerson, on_delete=models.CASCADE, blank=True, null=True
|
||||
)
|
||||
|
||||
|
||||
class SCHAOfficer(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
position = models.CharField(max_length=255)
|
||||
email = models.EmailField(max_length=255)
|
||||
|
||||
# class CommunitySchools(models.Model):
|
||||
# name = models.CharField(max_length=255)
|
||||
# name_school_url = models.URLField(max_length=256)
|
||||
# rating_url = models.URLField(max_length=256)
|
||||
# rating = models.DecimalField(max_digits=5, decimal_places=2)
|
||||
# distance = models.DecimalField(max_digits=3, decimal_places=1)
|
||||
# principal = models.CharField(max_length=255)
|
||||
# grades = models.CharField(max_length=255)
|
||||
|
||||
# class CommunityShoppingAndDining(models.Model):
|
||||
# name = models.CharField(max_length=255)
|
||||
# distance = models.DecimalField(max_digits=3, decimal_places=1)
|
||||
# description = models.CharField(max_length=1024)
|
||||
|
||||
# class CommunityParks(models.Model):
|
||||
# name = models.CharField(max_length=255)
|
||||
# distance = models.DecimalField(max_digits=3, decimal_places=1)
|
||||
# description = models.CharField(max_length=1024)
|
||||
47
schasite/static/css/style2.css
Normal file
47
schasite/static/css/style2.css
Normal file
@@ -0,0 +1,47 @@
|
||||
/* Custom Styles */
|
||||
:root {
|
||||
--bs-success-rgb: 40, 167, 69;
|
||||
}
|
||||
|
||||
body {
|
||||
padding-top: 56px;
|
||||
}
|
||||
|
||||
section {
|
||||
scroll-margin-top: 70px;
|
||||
}
|
||||
|
||||
/* Hero Section */
|
||||
#home {
|
||||
background-color: rgba(var(--bs-success-rgb), 0.1);
|
||||
}
|
||||
|
||||
/* News and Contact Sections */
|
||||
.bg-success-10 {
|
||||
background-color: rgba(var(--bs-success-rgb), 0.1);
|
||||
}
|
||||
|
||||
/* Card hover effect */
|
||||
.card {
|
||||
transition: transform 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
/* Contact icons */
|
||||
.bi {
|
||||
vertical-align: -.125em;
|
||||
}
|
||||
|
||||
/* Form validation */
|
||||
.was-validated .form-control:invalid,
|
||||
.form-control.is-invalid {
|
||||
border-color: #dc3545;
|
||||
}
|
||||
|
||||
.was-validated .form-control:valid,
|
||||
.form-control.is-valid {
|
||||
border-color: #198754;
|
||||
}
|
||||
276
schasite/templates/schasite/about_us2.html
Normal file
276
schasite/templates/schasite/about_us2.html
Normal file
@@ -0,0 +1,276 @@
|
||||
{% extends 'schasite/base2.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block pagetitle %}
|
||||
<title>Stonehedge Community Homeowners Association</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<section id="schools" class="py-5">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6 mb-4 mb-lg-0">
|
||||
<img src="../images/schools.jpg" alt="Local Schools" class="img-fluid rounded shadow">
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<h2 class="text-success mb-4">Excellent Schools Serving Our Community</h2>
|
||||
<p>Stonehedge Community Homeowners Association is proud to be served by some of the highest-rated schools in the district, providing quality education for all ages.</p>
|
||||
|
||||
<div class="accordion mt-4" id="schoolsAccordion">
|
||||
<div class="accordion-item">
|
||||
<h3 class="accordion-header" id="headingOne">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne">
|
||||
Whittier Elementary School
|
||||
</button>
|
||||
</h3>
|
||||
<div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#schoolsAccordion">
|
||||
<div class="accordion-body">
|
||||
<p><strong>Rating:</strong> 87.5/100 (<a href="https://www.usnews.com/education/k12/illinois/whittier-elementary-school-249318">U.S. News</a>)<br>
|
||||
<strong>Grades:</strong> K-5<br>
|
||||
<strong>Enrollment:</strong> 414<br>
|
||||
<strong>Distance:</strong> 2.3 miles from community entrance<br>
|
||||
<strong>Principal:</strong> Robert Cerny</p>
|
||||
<a href="https://www.cusd200.org/o/whittier" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h3 class="accordion-header" id="headingTwo">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo">
|
||||
Edison Middle School
|
||||
</button>
|
||||
</h3>
|
||||
<div id="collapseTwo" class="accordion-collapse collapse" data-bs-parent="#schoolsAccordion">
|
||||
<div class="accordion-body">
|
||||
<p><strong>Rating:</strong> 75.69/10 (<a href="https://www.usnews.com/education/k12/illinois/edison-middle-school-262446">U.S. News</a>)<br>
|
||||
<strong>Grades:</strong> 6-8<br>
|
||||
<strong>Enrollment:</strong> 674<br>
|
||||
<strong>Distance:</strong> 2.5 miles from community entrance<br>
|
||||
<strong>Principal:</strong> Rachel Bednar</p>
|
||||
<a href="https://www.cusd200.org/o/edison" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h3 class="accordion-header" id="headingThree">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree">
|
||||
Wheaton Warrenville South High School
|
||||
</button>
|
||||
</h3>
|
||||
<div id="collapseThree" class="accordion-collapse collapse" data-bs-parent="#schoolsAccordion">
|
||||
<div class="accordion-body">
|
||||
<p><strong>Rating:</strong> 90.43/10 (<a href="https://www.usnews.com/education/best-high-schools/illinois/districts/community-unified-school-district-200/wheaton-warrenville-south-high-school-7074">U.S. News</a>)<br>
|
||||
<strong>Grades:</strong> 9-12<br>
|
||||
<strong>Enrollment:</strong> 1845<br>
|
||||
<strong>Distance:</strong> 3.1 miles from community entrance<br>
|
||||
<strong>Principal:</strong> Lorie Campos</p>
|
||||
<a href="https://www.cusd200.org/o/wheatonwarrenvillesouth" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h3 class="accordion-header" id="headingFour">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour">
|
||||
St. Michael Parish School
|
||||
</button>
|
||||
</h3>
|
||||
<div id="collapseFour" class="accordion-collapse collapse" data-bs-parent="#schoolsAccordion">
|
||||
<div class="accordion-body">
|
||||
<strong>Grades:</strong> Pre-K-8<br>
|
||||
<strong>Distance:</strong> 2.5 miles from community entrance<br>
|
||||
<strong>Principal:</strong> Adam Furgson</p>
|
||||
<a href="https://www.stmichaelschoolwheaton.org/" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h3 class="accordion-header" id="headingFive">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFive">
|
||||
St. Francis High School
|
||||
</button>
|
||||
</h3>
|
||||
<div id="collapseFive" class="accordion-collapse collapse" data-bs-parent="#schoolsAccordion">
|
||||
<div class="accordion-body">
|
||||
<strong>Grades:</strong> 9-12<br>
|
||||
<strong>Enrollment:</strong> 698<br>
|
||||
<strong>Distance:</strong> 2.7 miles from community entrance<br>
|
||||
<strong>Principal:</strong> TBD</p>
|
||||
<a href="https://www.sfhscollegeprep.org/" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h3 class="accordion-header" id="headingSix">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSix">
|
||||
Benet Academy High School
|
||||
</button>
|
||||
</h3>
|
||||
<div id="collapseSix" class="accordion-collapse collapse" data-bs-parent="#schoolsAccordion">
|
||||
<div class="accordion-body">
|
||||
<strong>Grades:</strong> 9-12<br>
|
||||
<strong>Enrollment:</strong> 1265<br>
|
||||
<strong>Distance:</strong> 5.9 miles from community entrance<br>
|
||||
<strong>Principal:</strong> TBD</p>
|
||||
<a href="http://www.cusd200.org/Domain/140" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- History Section -->
|
||||
<section id="history" class="py-5 bg-success bg-opacity-10">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6 order-lg-2 mb-4 mb-lg-0">
|
||||
<img src="../images/history.jpg" alt="Community History" class="img-fluid rounded shadow">
|
||||
</div>
|
||||
<div class="col-lg-6 order-lg-1">
|
||||
<h2 class="text-success mb-4">Our Rich History</h2>
|
||||
|
||||
<div class="timeline mt-4">
|
||||
<div class="timeline-item">
|
||||
<div class="timeline-badge bg-success"></div>
|
||||
<div class="timeline-panel">
|
||||
<div class="timeline-heading">
|
||||
<h4 class="timeline-title">1977</h4>
|
||||
</div>
|
||||
<div class="timeline-body">
|
||||
<p>Levitt Homes began building the Stonehedge Subdivision. In 1978, Levitt had financial problems and construction was halted for many months until local builders Joe Keim, Ed Keim and Faganel Builders took over the development of the Stonehedge subdivision in South Wheaton. Today these attractive residences are much in demand. The Orchard Cove subdivision was established in 1985 and development began in the Marywood subdivision in 1994. Both Orchard Cove & Marywood were built by Keim. </p>
|
||||
<p>hese subdivisions offer a variety of spacious, two-story Georgian, Tudor, Victorian and salt box style homes, mixed with a limited number of contemporaries, split levels and ranches.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Local Shopping Section -->
|
||||
<section id="shopping" class="py-5">
|
||||
<div class="container">
|
||||
<h2 class="text-center text-success mb-5">Local Shopping & Dining</h2>
|
||||
<p class="text-center mb-5">Stonehedge Community Homeowners Association is conveniently located near excellent shopping and dining options to meet all your needs.</p>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<img src="../images/greenwood-mall.jpg" class="card-img-top" alt="Greenwood Mall">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Danada</h5>
|
||||
<p class="card-text"><i class="bi bi-geo-alt-fill text-success me-2"></i>1.0 miles from community</p>
|
||||
<p class="card-text">Over 10 stores including major department stores, fashion retailers, and specialty shops.</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent">
|
||||
<small class="text-muted">Open Mon-Sat 10am-9pm, Sun 11am-7pm</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<img src="../images/farmers-market.jpg" class="card-img-top" alt="Farmers Market">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Downtown Wheaton</h5>
|
||||
<p class="card-text"><i class="bi bi-geo-alt-fill text-success me-2"></i>2.7 miles from community</p>
|
||||
<p class="card-text">Fresh local produce, artisanal foods, and handmade goods every Saturday morning.</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent">
|
||||
<small class="text-muted">Saturdays 7am-1pm, May through October</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<img src="../images/farmers-market.jpg" class="card-img-top" alt="Farmers Market">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Wheaton Farmersmarket</h5>
|
||||
<p class="card-text"><i class="bi bi-geo-alt-fill text-success me-2"></i>2.8 miles from community</p>
|
||||
<p class="card-text">Fresh local produce, artisanal foods, and handmade goods every Saturday morning.</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent">
|
||||
<small class="text-muted">Saturdays 7am-1pm, May through October</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<img src="../images/dining-district.jpg" class="card-img-top" alt="Dining District">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Downtown Naperville</h5>
|
||||
<p class="card-text"><i class="bi bi-geo-alt-fill text-success me-2"></i>6.2 miles from community</p>
|
||||
<p class="card-text">Diverse restaurants featuring Italian, Asian, Mexican, and American cuisine options.</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent">
|
||||
<small class="text-muted">Various hours - over 25 restaurants</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Parks & Recreation Section -->
|
||||
<section id="parks" class="py-5 bg-success bg-opacity-10">
|
||||
<div class="container">
|
||||
<h2 class="text-center text-success mb-5">Parks</h2>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-6">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="row g-0 h-100">
|
||||
<div class="col-md-5">
|
||||
<img src="../images/community-park.jpg" class="img-fluid rounded-start h-100" alt="Community Park" style="object-fit: cover;">
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Brighton Park</h5>
|
||||
<p class="card-text"><i class="bi bi-geo-alt-fill text-success me-2"></i>Within SCHA</p>
|
||||
<ul class="list-group list-group-flush mb-3">
|
||||
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Playground</li>
|
||||
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Picninc Tables</li>
|
||||
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Dogs Allowed</li>
|
||||
</ul>
|
||||
<p class="card-text"><small class="text-muted">Open daily from dawn to dusk</small></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="row g-0 h-100">
|
||||
<div class="col-md-5">
|
||||
<img src="../images/nature-preserve.jpg" class="img-fluid rounded-start h-100" alt="Nature Preserve" style="object-fit: cover;">
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Seven Gables Park</h5>
|
||||
<p class="card-text"><i class="bi bi-geo-alt-fill text-success me-2"></i>0.5 miles from SCHA</p>
|
||||
<p>66.5 acres featuring sports fields, tennis courts & an ice rink, plus other recreational facilities.</p>
|
||||
<ul class="list-group list-group-flush mb-3">
|
||||
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Playground</li>
|
||||
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Baseball Field</li>
|
||||
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Tennis Court</li>
|
||||
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>BasketballCourt</li>
|
||||
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Picninc Tables</li>
|
||||
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Dogs Allowed</li>
|
||||
</ul>
|
||||
<p class="card-text"><small class="text-muted">Open daily from 8am to sunset</small></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% endblock %}
|
||||
90
schasite/templates/schasite/base2.html
Normal file
90
schasite/templates/schasite/base2.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
{% load static %}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
{% block pagetitle %}
|
||||
{% endblock %}
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<!-- Bootstrap Icons -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||
<!-- Custom CSS -->
|
||||
<link href="{% static 'css/style2.css' %}" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-success sticky-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="{% url 'index2' %}">Stonehedge Community Homeowners Association</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'index2' %}">Home</a>
|
||||
</li>
|
||||
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'about_us2' %}">About Us</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'calendar2' %}">Calendar</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'dues2' %}">Pay Dues</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'membership_form2' %}">Join Today</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'scha_board2' %}">SCHA Board</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'useful_links2' %}">Useful Links</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="bg-dark text-white pt-5 pb-4">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-6 mb-4 mb-lg-0">
|
||||
<h5 class="text-uppercase mb-4">Stonehedge Community Homeowners Association</h5>
|
||||
<p class="text-white-50">1234 Greenwood Lane<br>Anytown, ST 12345</p>
|
||||
</div>
|
||||
<div class="col-lg-4 offset-lg-2 mb-4 mb-lg-0">
|
||||
<h5 class="text-uppercase mb-4">Quick Links</h5>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2"><a href="{% url 'index2' %}" class="text-white-50 text-decoration-none">Home</a></li>
|
||||
<li class="mb-2"><a href="{% url 'about_us2' %}" class="text-white-50 text-decoration-none">About</a></li>
|
||||
<li class="mb-2"><a href="{% url 'calendar2' %}" class="text-white-50 text-decoration-none">Calendar</a></li>
|
||||
<li class="mb-2"><a href="{% url 'dues2' %}" class="text-white-50 text-decoration-none">Dues</a></li>
|
||||
<li class="mb-2"><a href="{% url 'membership_form2' %}" class="text-white-50 text-decoration-none">Membership Form</a></li>
|
||||
<li class="mb-2"><a href="{% url 'scha_board2' %}" class="text-white-50 text-decoration-none">SCHA Board</a></li>
|
||||
<li class="mb-2"><a href="{% url 'useful_links2' %}" class="text-white-50 text-decoration-none">Links</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="my-4 text-white-50">
|
||||
<div class="row align-items-center">
|
||||
<div id="footer">© Stonehedge Community Homeowners Association 2010-<script>document.write( new Date().getFullYear() );</script>. All rights reserved
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Custom JS -->
|
||||
{% block extra_js %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
202
schasite/templates/schasite/calendar2.html
Normal file
202
schasite/templates/schasite/calendar2.html
Normal file
@@ -0,0 +1,202 @@
|
||||
{% extends 'schasite/base2.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block pagetitle %}
|
||||
<title>Stonehedge Community Homeowners Association</title>
|
||||
<style type="text/css">
|
||||
/* Calendar Page Specific Styles */
|
||||
.event-card {
|
||||
transition: transform 0.3s ease;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.event-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.event-card .card-header {
|
||||
padding: 0.75rem 1.25rem;
|
||||
}
|
||||
|
||||
.event-card img {
|
||||
height: 180px;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
color: var(--bs-success);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
color: var(--bs-success);
|
||||
border-color: var(--bs-success);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.event-card img {
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.event-card .col-md-4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Icon alignment */
|
||||
.bi-calendar-event,
|
||||
.bi-calendar-range,
|
||||
.bi-clock,
|
||||
.bi-geo-alt,
|
||||
.bi-house,
|
||||
.bi-person,
|
||||
.bi-envelope {
|
||||
width: 1.25rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="py-5">
|
||||
<div class="container">
|
||||
<!-- Calendar Navigation -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<ul class="nav nav-tabs" id="calendarTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="upcoming-tab" data-bs-toggle="tab" data-bs-target="#upcoming" type="button" role="tab">
|
||||
Upcoming Events
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="past-tab" data-bs-toggle="tab" data-bs-target="#past" type="button" role="tab">
|
||||
Past Events
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Calendar Content -->
|
||||
<div class="tab-content" id="calendarTabContent">
|
||||
<!-- Upcoming Events Tab -->
|
||||
<div class="tab-pane fade show active" id="upcoming" role="tabpanel">
|
||||
<div class="row g-4">
|
||||
{% for event in future_events %}
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h3 class="h5 mb-0">{{ event.event_name }}</h3>
|
||||
<span class="badge bg-light text-success">Upcoming</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<img src="../images/events/picnic.jpg" alt="Community Picnic" class="img-fluid rounded mb-3 mb-md-0">
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="mb-3">
|
||||
<p class="mb-1"><i class="bi bi-calendar-event text-success me-2"></i><strong>Date:</strong> {{ event.start_date }}</p>
|
||||
<p class="mb-1"><i class="bi bi-clock text-success me-2"></i><strong>Time:</strong> 11:00 AM - 3:00 PM</p>
|
||||
<p class="mb-1"><i class="bi bi-geo-alt text-success me-2"></i><strong>Location:</strong> {{ event.location_name }}</p>
|
||||
<p class="mb-1"><i class="bi bi-house text-success me-2"></i><strong>Address:</strong> </p>
|
||||
<p class="mb-1"><i class="bi bi-person text-success me-2"></i><strong>Coordinator:</strong> {{ event.coordinator_name }}</p>
|
||||
<p class="mb-1"><i class="bi bi-envelope text-success me-2"></i><strong>Email:</strong> <a href="mailto:{{ event.coordinator_email }}">{{ event.coordinator_email }}</a></p>
|
||||
</div>
|
||||
<p>{{ event.description }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<a href="{{ event.event_url }}" class="btn btn-sm btn-success">{{ event.event_link_name }}</a>
|
||||
{% if event.rsvp_date %}
|
||||
<small class="text-muted">RSVP by {{ event.rsvp_date }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Past Events Tab -->
|
||||
<div class="tab-pane fade" id="past" role="tabpanel">
|
||||
<div class="row g-4">
|
||||
<!-- Past Event 1 -->
|
||||
{% for event in future_events %}
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-header bg-secondary text-white">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<h3 class="h5 mb-0">{{ event.event_name }}</h3>
|
||||
<span class="badge bg-light text-secondary">Completed</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<img src="../images/events/spring-cleanup.jpg" alt="Spring Cleanup" class="img-fluid rounded mb-3 mb-md-0">
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="mb-3">
|
||||
<p class="mb-1"><i class="bi bi-calendar-event text-secondary me-2"></i><strong>Date:</strong> {{ event.start_date }}</p>
|
||||
<p class="mb-1"><i class="bi bi-clock text-secondary me-2"></i><strong>Time:</strong> 9:00 AM - 12:00 PM</p>
|
||||
<p class="mb-1"><i class="bi bi-geo-alt text-secondary me-2"></i><strong>Location:</strong> {{ event.location_name }}</p>
|
||||
<p class="mb-1"><i class="bi bi-house text-secondary me-2"></i><strong>Address:</strong> </p>
|
||||
<p class="mb-1"><i class="bi bi-person text-secondary me-2"></i><strong>Coordinator:</strong> {{ event.coordinator_name }}</p>
|
||||
<p class="mb-1"><i class="bi bi-envelope text-secondary me-2"></i><strong>Email:</strong> <a href="mailto:{{ event.coordinator_email }}">{{ event.coordinator_email }}</a></p>
|
||||
</div>
|
||||
<p>{{ event.description }}</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<a href="https://greenwoodestates.org/events/spring-cleanup-2024" class="btn btn-sm btn-secondary">Event Recap</a>
|
||||
<small class="text-muted">52 volunteers participated</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// This would be replaced with actual event loading from a database
|
||||
console.log('Calendar page loaded');
|
||||
|
||||
// Example: Highlight current tab in URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const tab = urlParams.get('tab');
|
||||
if (tab === 'past') {
|
||||
const pastTab = new bootstrap.Tab(document.getElementById('past-tab'));
|
||||
pastTab.show();
|
||||
}
|
||||
|
||||
// Example: Add event to Google Calendar
|
||||
document.querySelectorAll('.add-to-calendar').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
// In a real implementation, this would generate a calendar event
|
||||
alert('This would add the event to your calendar');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
230
schasite/templates/schasite/dues2.html
Normal file
230
schasite/templates/schasite/dues2.html
Normal file
@@ -0,0 +1,230 @@
|
||||
{% extends 'schasite/base2.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block pagetitle %}
|
||||
<title>Stonehedge Community Homeowners Association</title>
|
||||
<style type="text/css">
|
||||
/* Payment Page Specific Styles */
|
||||
.payment-option-card {
|
||||
transition: transform 0.3s ease;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.payment-option-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.payment-option-card .bi {
|
||||
font-size: 3rem;
|
||||
}
|
||||
|
||||
/* Table styles */
|
||||
.table thead th {
|
||||
background-color: var(--bs-success);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.payment-option-card {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.table-responsive {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Stripe button styling */
|
||||
.btn-stripe {
|
||||
background-color: #635bff;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-stripe:hover {
|
||||
background-color: #4a42d1;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="py-5">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<!-- Payment Information Card -->
|
||||
<div class="card shadow-sm mb-5">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="mb-0">Payment Information</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle-fill me-2"></i>
|
||||
You will be redirected to our secure payment processor (Stripe) to complete your transaction.
|
||||
</div>
|
||||
|
||||
<h4 class="text-success mb-4">Annual Dues</h4>
|
||||
|
||||
<div class="table-responsive mb-4">
|
||||
<table class="table table-bordered">
|
||||
<thead class="table-success">
|
||||
<tr>
|
||||
<th>Payment Option</th>
|
||||
<th>Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Full Payment (Annual)</td>
|
||||
<td>$30.00</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- <div class="mb-4 p-3 bg-light rounded">
|
||||
<h5 class="text-success"><i class="bi bi-house-check-fill me-2"></i>Property Information</h5>
|
||||
<p><strong>Member:</strong> John Doe (Lot #42)</p>
|
||||
<p><strong>Current Balance:</strong> $600.00 (Due January 15, 2024)</p>
|
||||
<p><strong>Last Payment:</strong> $600.00 on January 10, 2023</p>
|
||||
<a href="#" class="btn btn-sm btn-outline-success">View Payment History</a>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Payment Options -->
|
||||
<div class="card shadow-sm mb-5">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="mb-0">Payment Options</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-4">
|
||||
<!-- Credit/Debit Card -->
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 border-success">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-credit-card text-success display-4 mb-3"></i>
|
||||
<h4 class="text-success">Credit/Debit Card</h4>
|
||||
<p class="card-text">Pay securely with Visa, Mastercard, American Express, or Discover.</p>
|
||||
<a href="https://buy.stripe.com/test_14k6rE7jD3hQ2SQ144" class="btn btn-success mt-3" target="_blank" id="submitBtn">
|
||||
Pay with Card
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<!-- Other Payment Methods -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<h5 class="text-success mb-3">Other Payment Methods</h5>
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
<a href="#" class="btn btn-outline-success">
|
||||
<i class="bi bi-cash-coin me-2"></i>Mail a Check
|
||||
</a>
|
||||
<a href="#" class="btn btn-outline-success">
|
||||
<i class="bi bi-building me-2"></i>In-Person Payment
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Payment FAQ -->
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="mb-0">Payment FAQ</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="accordion" id="paymentFAQ">
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#faq1">
|
||||
Is there a fee for online payments?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="faq1" class="accordion-collapse collapse show" data-bs-parent="#paymentFAQ">
|
||||
<div class="accordion-body">
|
||||
No, the HOA absorbs all processing fees for online payments. You pay exactly your dues amount with no additional fees.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq2">
|
||||
When will my payment be reflected in my account?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="faq2" class="accordion-collapse collapse" data-bs-parent="#paymentFAQ">
|
||||
<div class="accordion-body">
|
||||
Credit/debit card payments are posted immediately.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq4">
|
||||
What payment methods do you accept?
|
||||
</button>
|
||||
</h2>
|
||||
<div id="faq4" class="accordion-collapse collapse" data-bs-parent="#paymentFAQ">
|
||||
<div class="accordion-body">
|
||||
Online we accept all major credit/debit cards (Visa, Mastercard, American Express, Discover). For in-person or mail payments, we accept checks or money orders made payable to "SCHA"
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Stripe Integration Script (example) -->
|
||||
<script>
|
||||
// This would be replaced with your actual Stripe integration code
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Example: Track outbound links to Stripe
|
||||
const stripeLinks = document.querySelectorAll('a[href*="stripe.com"]');
|
||||
stripeLinks.forEach(link => {
|
||||
link.addEventListener('click', function(e) {
|
||||
// You could add analytics tracking here
|
||||
console.log('Redirecting to Stripe payment');
|
||||
});
|
||||
});
|
||||
|
||||
// Example: Dynamic balance loading (would connect to your backend)
|
||||
function loadAccountBalance() {
|
||||
// In a real implementation, this would fetch from your database
|
||||
// This is just a placeholder example
|
||||
return {
|
||||
member: "John Doe (Lot #42)",
|
||||
balance: "$600.00",
|
||||
dueDate: "January 15, 2024",
|
||||
lastPayment: "$600.00 on January 10, 2023"
|
||||
};
|
||||
}
|
||||
|
||||
// Update the account info section (example)
|
||||
const accountInfo = loadAccountBalance();
|
||||
document.querySelector('.bg-light p:nth-child(1)').innerHTML =
|
||||
`<strong>Member:</strong> ${accountInfo.member}`;
|
||||
document.querySelector('.bg-light p:nth-child(2)').innerHTML =
|
||||
`<strong>Current Balance:</strong> ${accountInfo.balance} (Due ${accountInfo.dueDate})`;
|
||||
document.querySelector('.bg-light p:nth-child(3)').innerHTML =
|
||||
`<strong>Last Payment:</strong> ${accountInfo.lastPayment}`;
|
||||
});
|
||||
</script>
|
||||
<script src="{% static 'main.js' %}"></script>
|
||||
{% endblock %}
|
||||
208
schasite/templates/schasite/index2.html
Normal file
208
schasite/templates/schasite/index2.html
Normal file
@@ -0,0 +1,208 @@
|
||||
{% extends 'schasite/base2.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block pagetitle %}
|
||||
<title>Stonehedge Community Homeowners Association</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Hero Section -->
|
||||
<section id="home" class="py-5 bg-success bg-opacity-10">
|
||||
<div class="container py-5">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-8 mb-4 mb-lg-0">
|
||||
<h1 class="display-4 fw-bold mb-3">Welcome to Stonehedge Community Homeowners Association</h1>
|
||||
<p class="lead mb-4">A premier community dedicated to maintaining beautiful homes, safe neighborhoods, and a high quality of life for all residents.</p>
|
||||
<a href="{% url 'about_us2' %}" class="btn btn-success btn-lg px-4">Learn More</a>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<img src='{% static "images/bricks.jpg" %}' alt="Stonehedge Community Homeowners Association" class="img-fluid rounded-circle shadow">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- About Section -->
|
||||
<section id="about" class="py-5">
|
||||
<div class="container">
|
||||
<h2 class="text-center text-success mb-5">About Our Community</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<img src="images/amenities.jpg" class="card-img-top" alt="Community Amenities" style="height: 200px; object-fit: cover;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Amenities</h5>
|
||||
<p class="card-text">Our community features a swimming pool, playground, walking trails, and clubhouse available for resident use.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<img src="images/board.jpg" class="card-img-top" alt="HOA Board" style="height: 200px; object-fit: cover;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">HOA Board</h5>
|
||||
<p class="card-text">Meet our elected board members who volunteer their time to maintain and improve our neighborhood.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<img src="images/rules.jpg" class="card-img-top" alt="Community Rules" style="height: 200px; object-fit: cover;">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Community Rules</h5>
|
||||
<p class="card-text">Learn about our community guidelines designed to maintain property values and quality of life for all residents.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- News Section -->
|
||||
<section id="news" class="py-5 bg-success bg-opacity-10">
|
||||
<div class="container">
|
||||
<h2 class="text-center text-success mb-5">Community News & Events</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Annual Community Picnic</h5>
|
||||
<p class="card-text">Join us on June 15th for our annual community picnic at the clubhouse. Food, games, and fun for the whole family!</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0">
|
||||
<a href="#" class="btn btn-outline-success">RSVP Now</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Landscaping Updates</h5>
|
||||
<p class="card-text">New landscaping will be installed in common areas beginning next month. Expect temporary parking restrictions.</p>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0">
|
||||
<a href="#" class="btn btn-outline-success">View Schedule</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-4">
|
||||
<a href="#" class="btn btn-success px-4">View All News</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Documents Section -->
|
||||
<section id="documents" class="py-5">
|
||||
<div class="container">
|
||||
<h2 class="text-center text-success mb-5">Community Documents</h2>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="mb-0">Governing Documents</h5>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a href="#" class="list-group-item list-group-item-action">CC&Rs</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Bylaws</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Articles of Incorporation</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="mb-0">Meeting Minutes</h5>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a href="#" class="list-group-item list-group-item-action">2023 Meetings</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">2022 Meetings</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Archive</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="mb-0">Forms</h5>
|
||||
</div>
|
||||
<div class="list-group list-group-flush">
|
||||
<a href="#" class="list-group-item list-group-item-action">Architectural Request</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Complaint Form</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Resale Certificate Request</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Contact Section -->
|
||||
<!-- <section id="contact" class="py-5 bg-success bg-opacity-10">
|
||||
<div class="container">
|
||||
<h2 class="text-center text-success mb-5">Contact Us</h2>
|
||||
<div class="row g-4 mb-5">
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 text-center shadow-sm">
|
||||
<div class="card-body py-4">
|
||||
<i class="bi bi-envelope-fill text-success fs-1 mb-3"></i>
|
||||
<h5 class="card-title">Email Us</h5>
|
||||
<p class="card-text">board@greenwoodestates.org</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card h-100 text-center shadow-sm">
|
||||
<div class="card-body py-4">
|
||||
<i class="bi bi-telephone-fill text-success fs-1 mb-3"></i>
|
||||
<h5 class="card-title">Call Us</h5>
|
||||
<p class="card-text">(555) 123-4567</p>
|
||||
<p class="card-text">Monday-Friday, 9am-5pm</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<form class="needs-validation" novalidate>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<label for="name" class="form-label">Name</label>
|
||||
<input type="text" class="form-control" id="name" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide your name.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide a valid email.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="subject" class="form-label">Subject</label>
|
||||
<input type="text" class="form-control" id="subject" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide a subject.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<label for="message" class="form-label">Message</label>
|
||||
<textarea class="form-control" id="message" rows="4" required></textarea>
|
||||
<div class="invalid-feedback">
|
||||
Please enter your message.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 text-center">
|
||||
<button class="btn btn-success btn-lg px-4" type="submit">
|
||||
<i class="bi bi-send-fill me-2"></i>Send Message
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</section>
|
||||
{% endblock %}
|
||||
414
schasite/templates/schasite/membership_form2.html
Normal file
414
schasite/templates/schasite/membership_form2.html
Normal file
@@ -0,0 +1,414 @@
|
||||
{% extends 'schasite/base2.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block pagetitle %}
|
||||
<title>Stonehedge Community Homeowners Association</title>
|
||||
<script type="text/css">
|
||||
/* Membership Form Specific Styles */
|
||||
#membershipForm fieldset {
|
||||
margin-bottom: 2rem;
|
||||
padding: 1.5rem;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.375rem;
|
||||
}
|
||||
|
||||
#membershipForm legend {
|
||||
width: auto;
|
||||
padding: 0 0.5rem;
|
||||
margin-bottom: 0;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.form-check-label {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
#membershipForm fieldset {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
#membershipForm legend {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Form validation styling */
|
||||
.was-validated .form-control:valid,
|
||||
.was-validated .form-control.is-valid {
|
||||
border-color: #198754;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.was-validated .form-control:invalid,
|
||||
.was-validated .form-control.is-invalid {
|
||||
border-color: #dc3545;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
/* Modal styling */
|
||||
.modal-content {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
background-color: var(--bs-success);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.modal-header .btn-close {
|
||||
filter: invert(1);
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="py-5">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h2 class="h4 mb-0">New Member Information</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="membershipForm" class="needs-validation" novalidate>
|
||||
<!-- Household Information -->
|
||||
<fieldset class="mb-4">
|
||||
<legend class="h5 text-success border-bottom pb-2">Household Information</legend>
|
||||
|
||||
<!-- Address Section -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<label for="streetAddress" class="form-label">Street Address*</label>
|
||||
<input type="text" class="form-control" id="streetAddress" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide your street address.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="unit" class="form-label">Unit/Apt #</label>
|
||||
<input type="text" class="form-control" id="unit">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="city" class="form-label">City*</label>
|
||||
<input type="text" class="form-control" id="city" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide your city.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="state" class="form-label">State*</label>
|
||||
<select class="form-select" id="state" required>
|
||||
<option value="" selected disabled>Choose...</option>
|
||||
<option>Alabama</option>
|
||||
<option>Alaska</option>
|
||||
<!-- Add all states -->
|
||||
<option>Wyoming</option>
|
||||
</select>
|
||||
<div class="invalid-feedback">
|
||||
Please select your state.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label for="zipCode" class="form-label">ZIP Code*</label>
|
||||
<input type="text" class="form-control" id="zipCode" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide your ZIP code.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Primary Member -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-12">
|
||||
<h5 class="text-success">Primary Member*</h5>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="firstName1" class="form-label">First Name*</label>
|
||||
<input type="text" class="form-control" id="firstName1" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide first name.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="lastName1" class="form-label">Last Name*</label>
|
||||
<input type="text" class="form-control" id="lastName1" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide last name.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="email1" class="form-label">Email*</label>
|
||||
<input type="email" class="form-control" id="email1" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide a valid email.
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="phone1" class="form-label">Phone*</label>
|
||||
<input type="tel" class="form-control" id="phone1" required>
|
||||
<div class="invalid-feedback">
|
||||
Please provide phone number.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Secondary Member (Optional) -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="addSecondMember">
|
||||
<label class="form-check-label" for="addSecondMember">
|
||||
Add Second Household Member
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="secondMemberSection" class="row g-3 mb-4" style="display: none;">
|
||||
<div class="col-12">
|
||||
<h5 class="text-success">Secondary Member</h5>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="firstName2" class="form-label">First Name</label>
|
||||
<input type="text" class="form-control" id="firstName2">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="lastName2" class="form-label">Last Name</label>
|
||||
<input type="text" class="form-control" id="lastName2">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="email2" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email2">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="phone2" class="form-label">Phone</label>
|
||||
<input type="tel" class="form-control" id="phone2">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- Committees Section -->
|
||||
<fieldset class="mb-4">
|
||||
<legend class="h5 text-success border-bottom pb-2">Committee Interests</legend>
|
||||
<p>Check all committees you're interested in joining:</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="socialCommittee">
|
||||
<label class="form-check-label" for="socialCommittee">
|
||||
Social & Events Committee
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="landscapeCommittee">
|
||||
<label class="form-check-label" for="landscapeCommittee">
|
||||
Landscape & Maintenance Committee
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="safetyCommittee">
|
||||
<label class="form-check-label" for="safetyCommittee">
|
||||
Safety & Neighborhood Watch
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="architecturalCommittee">
|
||||
<label class="form-check-label" for="architecturalCommittee">
|
||||
Architectural Review Committee
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="communicationsCommittee">
|
||||
<label class="form-check-label" for="communicationsCommittee">
|
||||
Communications Committee
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="welcomingCommittee">
|
||||
<label class="form-check-label" for="welcomingCommittee">
|
||||
Welcoming Committee
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<label for="otherCommitteeInterest" class="form-label">Other interests or skills you'd like to contribute:</label>
|
||||
<textarea class="form-control" id="otherCommitteeInterest" rows="2"></textarea>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- Services Section -->
|
||||
<fieldset class="mb-4">
|
||||
<legend class="h5 text-success border-bottom pb-2">Services</legend>
|
||||
<p>Check if you're interested in providing these services to neighbors:</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="babysitting">
|
||||
<label class="form-check-label" for="babysitting">
|
||||
Babysitting
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="lawnMowing">
|
||||
<label class="form-check-label" for="lawnMowing">
|
||||
Lawn Mowing
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="snowShoveling">
|
||||
<label class="form-check-label" for="snowShoveling">
|
||||
Snow Shoveling
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="houseSitting">
|
||||
<label class="form-check-label" for="houseSitting">
|
||||
House Sitting
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="petCare">
|
||||
<label class="form-check-label" for="petCare">
|
||||
Pet Care
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="otherService">
|
||||
<label class="form-check-label" for="otherService">
|
||||
Other (please specify)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3" id="otherServiceSpecify" style="display: none;">
|
||||
<label for="otherServiceDetails" class="form-label">Please specify other service:</label>
|
||||
<input type="text" class="form-control" id="otherServiceDetails">
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- Terms & Submit -->
|
||||
<div class="mb-3">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="agreeTerms" required>
|
||||
<label class="form-check-label" for="agreeTerms">
|
||||
I agree to the <a href="#" data-bs-toggle="modal" data-bs-target="#termsModal">Terms and Conditions</a>*
|
||||
</label>
|
||||
<div class="invalid-feedback">
|
||||
You must agree before submitting.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||
<button class="btn btn-outline-secondary me-md-2" type="reset">Reset Form</button>
|
||||
<button class="btn btn-success" type="submit">Submit Membership</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Terms Modal -->
|
||||
<div class="modal fade" id="termsModal" tabindex="-1" aria-labelledby="termsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="termsModalLabel">Membership Terms and Conditions</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h6>Greenwood Estates HOA Membership Agreement</h6>
|
||||
<p>By joining the Greenwood Estates Homeowners Association, you agree to:</p>
|
||||
<ol>
|
||||
<li>Abide by the community covenants, conditions, and restrictions (CC&Rs)</li>
|
||||
<li>Pay annual membership dues in a timely manner</li>
|
||||
<li>Participate in community standards and guidelines</li>
|
||||
<li>Receive communications from the HOA board and committees</li>
|
||||
<li>Have your contact information shared with other members for community purposes</li>
|
||||
</ol>
|
||||
<p>Your membership helps maintain our community's quality and property values. The HOA board reserves the right to approve or deny membership applications.</p>
|
||||
<h6>Privacy Policy</h6>
|
||||
<p>Your personal information will be used solely for HOA communications and operations. We do not sell or share member information with third parties except as required for community management.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-success" data-bs-dismiss="modal">I Understand</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block extra_js %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Toggle second member section
|
||||
const addSecondMember = document.getElementById('addSecondMember');
|
||||
const secondMemberSection = document.getElementById('secondMemberSection');
|
||||
|
||||
addSecondMember.addEventListener('change', function() {
|
||||
if(this.checked) {
|
||||
secondMemberSection.style.display = 'flex';
|
||||
} else {
|
||||
secondMemberSection.style.display = 'none';
|
||||
// Clear second member fields when hidden
|
||||
document.getElementById('firstName2').value = '';
|
||||
document.getElementById('lastName2').value = '';
|
||||
document.getElementById('email2').value = '';
|
||||
document.getElementById('phone2').value = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle other service field
|
||||
const otherService = document.getElementById('otherService');
|
||||
const otherServiceSpecify = document.getElementById('otherServiceSpecify');
|
||||
|
||||
otherService.addEventListener('change', function() {
|
||||
otherServiceSpecify.style.display = this.checked ? 'block' : 'none';
|
||||
if(!this.checked) {
|
||||
document.getElementById('otherServiceDetails').value = '';
|
||||
}
|
||||
});
|
||||
|
||||
// Form validation
|
||||
const form = document.getElementById('membershipForm');
|
||||
|
||||
form.addEventListener('submit', function(event) {
|
||||
if (!form.checkValidity()) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
form.classList.add('was-validated');
|
||||
}, false);
|
||||
|
||||
// Phone number formatting
|
||||
const phoneInputs = document.querySelectorAll('input[type="tel"]');
|
||||
phoneInputs.forEach(input => {
|
||||
input.addEventListener('input', function() {
|
||||
this.value = this.value.replace(/[^0-9]/g, '');
|
||||
if(this.value.length > 10) {
|
||||
this.value = this.value.slice(0, 10);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
9
schasite/templates/schasite/newsletters2.html
Normal file
9
schasite/templates/schasite/newsletters2.html
Normal file
@@ -0,0 +1,9 @@
|
||||
{% extends 'schasite/base2.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block pagetitle %}
|
||||
<title>Stonehedge Community Homeowners Association</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
149
schasite/templates/schasite/scha_board2.html
Normal file
149
schasite/templates/schasite/scha_board2.html
Normal file
@@ -0,0 +1,149 @@
|
||||
{% extends 'schasite/base2.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block pagetitle %}
|
||||
<title>Stonehedge Community Homeowners Association</title>
|
||||
<style type="text/css">
|
||||
/* Board Page Specific Styles */
|
||||
.card-header h4 {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.board-member-img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
object-fit: cover;
|
||||
border: 3px solid var(--bs-success);
|
||||
}
|
||||
|
||||
.committee-card {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.committee-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.board-member-img {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="py-5">
|
||||
<div class="container">
|
||||
<!-- Board Information -->
|
||||
<div class="row justify-content-center mb-5">
|
||||
<div class="col-lg-10">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body text-center">
|
||||
<h3 class="text-success mb-3">2025-2026 Stonehedge Community Homeowners Association Board</h3>
|
||||
<p>The Stonehedge Community Homeowners Association is governed by a volunteer board of directors elected by the community members. Board members serve two-year terms and are responsible for overseeing the community's operations, finances, and enforcement of covenants.</p>
|
||||
<p>Board meetings are held monthly and are open to all residents. <a href="../index.html#documents">View meeting schedule</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<!-- Board Members -->
|
||||
<div class="row g-4">
|
||||
{% for officer in officers %}
|
||||
{% if officer %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-header bg-success text-white text-center py-3">
|
||||
<h4 class="mb-0">{{ officer.position }}</h4>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<img src="../images/board/john-smith.jpg" alt="John Smith" class="rounded-circle mb-3" width="150" height="150">
|
||||
<h5 class="card-title">{{ officer.name }}</h5>
|
||||
<!-- <p class="text-muted">Term: 2022-2024</p>
|
||||
<p class="card-text">John has lived in Greenwood Estates since 2015 and brings 20 years of financial management experience to the board.</p> -->
|
||||
<div class="d-flex justify-content-center">
|
||||
<a href="mailto:{{ officer.email }}" class="btn btn-sm btn-outline-success me-2">
|
||||
<i class="bi bi-envelope"></i> Email
|
||||
</a>
|
||||
<!-- <a href="tel:+15551234567" class="btn btn-sm btn-outline-success">
|
||||
<i class="bi bi-telephone"></i> Call
|
||||
</a> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card h-100 shadow-sm">
|
||||
<div class="card-header bg-success text-white text-center py-3">
|
||||
<h4 class="mb-0">TBD</h4>
|
||||
</div>
|
||||
<div class="card-body text-center">
|
||||
<img src="../images/board/john-smith.jpg" alt="John Smith" class="rounded-circle mb-3" width="150" height="150">
|
||||
<h5 class="card-title">TBD</h5>
|
||||
<p class="text-muted">TBD</p>
|
||||
<p class="card-text">TBD</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endfor %}
|
||||
|
||||
|
||||
<!-- Committees Section -->
|
||||
<div class="row mt-5">
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="mb-0">HOA Committees</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-success">Architectural Review</h5>
|
||||
<p class="card-text">Reviews all exterior modification requests to ensure compliance with community standards.</p>
|
||||
<p><strong>Chair:</strong> Robert Wilson</p>
|
||||
<a href="mailto:architecture@greenwoodestates.org" class="btn btn-sm btn-outline-success">Contact Committee</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-success">Social & Events</h5>
|
||||
<p class="card-text">Plans community events, holiday celebrations, and social gatherings.</p>
|
||||
<p><strong>Chair:</strong> Lisa Martinez</p>
|
||||
<a href="mailto:events@greenwoodestates.org" class="btn btn-sm btn-outline-success">Contact Committee</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-success">Landscape & Maintenance</h5>
|
||||
<p class="card-text">Oversees common area maintenance and landscaping improvements.</p>
|
||||
<p><strong>Chair:</strong> David Thompson</p>
|
||||
<a href="mailto:landscape@greenwoodestates.org" class="btn btn-sm btn-outline-success">Contact Committee</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt-3">
|
||||
<a href="#" class="btn btn-success">Learn About Committee Opportunities</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
||||
290
schasite/templates/schasite/useful_links2.html
Normal file
290
schasite/templates/schasite/useful_links2.html
Normal file
@@ -0,0 +1,290 @@
|
||||
{% extends 'schasite/base2.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block pagetitle %}
|
||||
<title>Stonehedge Community Homeowners Association</title>
|
||||
<script type="text/css">
|
||||
/* Links Page Specific Styles */
|
||||
.links-table th {
|
||||
background-color: var(--bs-success);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.links-table tr:hover {
|
||||
background-color: rgba(var(--bs-success-rgb), 0.05);
|
||||
}
|
||||
|
||||
/* Button styling */
|
||||
.btn-outline-success {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Card header icons */
|
||||
.card-header .bi {
|
||||
font-size: 1.2rem;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 767.98px) {
|
||||
.table-responsive {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main class="py-5">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-10">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="mb-0"><i class="bi bi-exclamation-triangle me-2"></i>Useful Links</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-success">
|
||||
<tr>
|
||||
<th>Resource</th>
|
||||
<th>Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for link in links %}
|
||||
<tr>
|
||||
<td>{{ link.name }}</td>
|
||||
<td><a href="{{ link.url }}" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Government Services -->
|
||||
<!-- <div class="card shadow-sm mb-5">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="mb-0"><i class="bi bi-building me-2"></i>Government Services</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-success">
|
||||
<tr>
|
||||
<th>Service</th>
|
||||
<th>Description</th>
|
||||
<th>Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>City of Anytown</td>
|
||||
<td>Official city website with services, permits, and local information</td>
|
||||
<td><a href="https://www.anytown.gov" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>County Services</td>
|
||||
<td>Property records, tax payments, and county resources</td>
|
||||
<td><a href="https://www.yourcounty.gov" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Waste Management</td>
|
||||
<td>Trash/recycling schedules, bulk pickup requests</td>
|
||||
<td><a href="https://www.waste-management.com/your-area" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Utility Company</td>
|
||||
<td>Pay bills, report outages, manage services</td>
|
||||
<td><a href="https://www.localutility.com" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Police Non-Emergency</td>
|
||||
<td>Local police department contact information</td>
|
||||
<td><a href="https://www.anytownpd.gov" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- Schools & Education -->
|
||||
<!-- <div class="card shadow-sm mb-5">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="mb-0"><i class="bi bi-book me-2"></i>Schools & Education</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-success">
|
||||
<tr>
|
||||
<th>School</th>
|
||||
<th>Description</th>
|
||||
<th>Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Greenwood Elementary</td>
|
||||
<td>Public elementary school serving our community</td>
|
||||
<td><a href="https://www.greenwood.edu/elementary" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Maplewood Middle School</td>
|
||||
<td>Public middle school serving our community</td>
|
||||
<td><a href="https://www.greenwood.edu/middle" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Central High School</td>
|
||||
<td>Public high school serving our community</td>
|
||||
<td><a href="https://www.greenwood.edu/high" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>School District</td>
|
||||
<td>District calendar, policies, and information</td>
|
||||
<td><a href="https://www.greenwood.edu" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Community Library</td>
|
||||
<td>Local library events and resources</td>
|
||||
<td><a href="https://www.anytownlibrary.org" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- Local Businesses -->
|
||||
<!-- <div class="card shadow-sm mb-5">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="mb-0"><i class="bi bi-shop me-2"></i>Local Businesses</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-success">
|
||||
<tr>
|
||||
<th>Business</th>
|
||||
<th>Description</th>
|
||||
<th>Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Greenwood Hardware</td>
|
||||
<td>Local hardware store with home improvement supplies</td>
|
||||
<td><a href="https://www.greenwoodhardware.com" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Anytown Grocers</td>
|
||||
<td>Local supermarket with delivery options</td>
|
||||
<td><a href="https://www.anytowngrocers.com" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Community Pharmacy</td>
|
||||
<td>Local pharmacy with prescription services</td>
|
||||
<td><a href="https://www.communitypharmacy.com" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Preferred Landscapers</td>
|
||||
<td>HOA-approved landscaping companies</td>
|
||||
<td><a href="https://www.hoalandscapers.com/greenwood" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Neighborhood Electrician</td>
|
||||
<td>Recommended electrician familiar with our homes</td>
|
||||
<td><a href="https://www.greenwoodelectric.com" target="_blank" class="btn btn-sm btn-outline-success">Visit Site</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<!-- Emergency Resources -->
|
||||
<!-- <div class="card shadow-sm">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h3 class="mb-0"><i class="bi bi-exclamation-triangle me-2"></i>Emergency Resources</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-success">
|
||||
<tr>
|
||||
<th>Resource</th>
|
||||
<th>Description</th>
|
||||
<th>Link</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Police/Fire Emergency</td>
|
||||
<td>Call 911 for life-threatening emergencies</td>
|
||||
<td><button class="btn btn-sm btn-danger">Call 911</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Poison Control</td>
|
||||
<td>24/7 poison emergency assistance</td>
|
||||
<td><a href="tel:1-800-222-1222" class="btn btn-sm btn-outline-success">Call 800-222-1222</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Animal Control</td>
|
||||
<td>Report stray or dangerous animals</td>
|
||||
<td><a href="tel:555-123-4567" class="btn btn-sm btn-outline-success">Call 555-123-4567</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Power Outage Reporting</td>
|
||||
<td>Report electrical outages in the area</td>
|
||||
<td><a href="tel:555-987-6543" class="btn btn-sm btn-outline-success">Call 555-987-6543</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gas Leak Reporting</td>
|
||||
<td>Report suspected natural gas leaks</td>
|
||||
<td><a href="tel:555-555-5555" class="btn btn-sm btn-outline-success">Call 555-555-5555</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- Links Page Script -->
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// This would be replaced with actual link tracking in production
|
||||
console.log('Useful links page loaded');
|
||||
|
||||
// Example: Track outbound links
|
||||
document.querySelectorAll('a[target="_blank"]').forEach(link => {
|
||||
link.addEventListener('click', function() {
|
||||
// In a real implementation, you might track this click
|
||||
console.log('Navigating to: ' + this.href);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -11,7 +11,16 @@ urlpatterns = [
|
||||
path("dues", views.dues, name="dues"),
|
||||
path("membership_form", views.membership_form, name="membership_form"),
|
||||
path("scha_board", views.scha_board, name="scha_board"),
|
||||
# updated UI urls
|
||||
path("index2", views.index2, name="index2"),
|
||||
path("about_us2", views.about_us2, name="about_us2"),
|
||||
path("calendar2", views.calendar2, name="calendar2"),
|
||||
path("newsletters2", views.newsletters2, name="newsletters2"),
|
||||
path("dues2", views.dues2, name="dues2"),
|
||||
path("membership_form2", views.membership_form2, name="membership_form2"),
|
||||
path("scha_board2", views.scha_board2, name="scha_board2"),
|
||||
path("useful_links2", views.useful_links2, name="useful_links2"),
|
||||
# stripe specific urls below
|
||||
path('config/', views.stripe_config),
|
||||
path('create-checkout-session/', views.create_checkout_session),
|
||||
]
|
||||
path("config/", views.stripe_config),
|
||||
path("create-checkout-session/", views.create_checkout_session),
|
||||
]
|
||||
|
||||
@@ -1,66 +1,80 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from .models import UsefulLinks, CalendarEvent, MembershipPerson, Payments
|
||||
from .forms import ChildrenForm, AddressForm, PeopleForm, CommitteeForm, ServicesForm#, CaptchaForm
|
||||
from .models import UsefulLinks, CalendarEvent, MembershipPerson, Payments, SCHAOfficer
|
||||
from .forms import (
|
||||
ChildrenForm,
|
||||
AddressForm,
|
||||
PeopleForm,
|
||||
CommitteeForm,
|
||||
ServicesForm,
|
||||
) # , CaptchaForm
|
||||
from django.db import transaction, IntegrityError
|
||||
|
||||
# Stripe required imports
|
||||
from django.conf import settings # new
|
||||
from django.conf import settings # new
|
||||
from django.http.response import JsonResponse, HttpResponse
|
||||
from django.views.decorators.csrf import csrf_exempt # new
|
||||
import stripe
|
||||
from django.views.decorators.csrf import csrf_exempt # new
|
||||
import stripe
|
||||
|
||||
"""
|
||||
Strip Stuff
|
||||
Tutorial: https://testdriven.io/blog/django-stripe-tutorial/
|
||||
"""
|
||||
|
||||
|
||||
def dues(request):
|
||||
return render(request, "schasite/dues.html", {})
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def stripe_config(request):
|
||||
if request.method == 'GET':
|
||||
stripe_config = {'publicKey': settings.STRIPE_PUBLISHABLE_KEY}
|
||||
if request.method == "GET":
|
||||
stripe_config = {"publicKey": settings.STRIPE_PUBLISHABLE_KEY}
|
||||
return JsonResponse(stripe_config, safe=False)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def create_checkout_session(request):
|
||||
if request.method == 'GET':
|
||||
if request.method == "GET":
|
||||
domain_url = "http://localhost:8000/"
|
||||
stripe.api_key = settings.STRIPE_SECRET_KEY
|
||||
try:
|
||||
checkout_session = stripe.checkout.Session.create(
|
||||
success_url = domain_url+'success?session_id={CHECKOUT_SESSION_ID}',
|
||||
cancel_url = domain_url+'cancelled/',
|
||||
payment_method_types=['card'],
|
||||
success_url=domain_url + "success?session_id={CHECKOUT_SESSION_ID}",
|
||||
cancel_url=domain_url + "cancelled/",
|
||||
payment_method_types=["card"],
|
||||
mode="payment",
|
||||
line_items = [{
|
||||
# 'name':'SCHA Dues',
|
||||
'quantity': 1,
|
||||
# 'currency': 'usd',
|
||||
'price': 'price_1OxZLfDV0RPXOyxG5ipjhUXk',
|
||||
}]
|
||||
line_items=[
|
||||
{
|
||||
# 'name':'SCHA Dues',
|
||||
"quantity": 1,
|
||||
# 'currency': 'usd',
|
||||
"price": "price_1OxZLfDV0RPXOyxG5ipjhUXk",
|
||||
}
|
||||
],
|
||||
)
|
||||
return JsonResponse({'sessionId': checkout_session['id']})
|
||||
return JsonResponse({"sessionId": checkout_session["id"]})
|
||||
except Exception as e:
|
||||
return JsonResponse({'error': str(e)})
|
||||
return JsonResponse({"error": str(e)})
|
||||
|
||||
|
||||
def stripe_success(request):
|
||||
return render(request, "schasite/dues_success.html", {})
|
||||
|
||||
|
||||
def stripe_cancelled(request):
|
||||
return render(request, "schasite/dues_cancelled.html", {})
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def stripe_webhook(request):
|
||||
stripe.api_key = settings.STRIPE_SECRET_KEY
|
||||
endpoint_secret = settings.STRIPE_ENDPOINT_SECRET
|
||||
payload = request.body
|
||||
sig_header = request.META['HTTP_STRIPE_SIGNATURE']
|
||||
sig_header = request.META["HTTP_STRIPE_SIGNATURE"]
|
||||
event = None
|
||||
|
||||
try:
|
||||
event = stripe.Webhook.construct_event(
|
||||
payload, sig_header, endpoint_secret
|
||||
)
|
||||
event = stripe.Webhook.construct_event(payload, sig_header, endpoint_secret)
|
||||
except ValueError as e:
|
||||
# Invalid payload
|
||||
return HttpResponse(status=400)
|
||||
@@ -69,78 +83,136 @@ def stripe_webhook(request):
|
||||
return HttpResponse(status=400)
|
||||
|
||||
# Handle the checkout.session.completed event
|
||||
if event['type'] == 'checkout.session.completed':
|
||||
if event["type"] == "checkout.session.completed":
|
||||
email = None
|
||||
try:
|
||||
email = event['data']['object']['customer_details']['email']
|
||||
email = event["data"]["object"]["customer_details"]["email"]
|
||||
except:
|
||||
pass
|
||||
person = MembershipPerson.objects.filter(email=email).first() # just take the first
|
||||
person = MembershipPerson.objects.filter(
|
||||
email=email
|
||||
).first() # just take the first
|
||||
|
||||
payment = Payments.objects.create(
|
||||
email=email,
|
||||
person = person
|
||||
)
|
||||
payment = Payments.objects.create(email=email, person=person)
|
||||
# try to link to a member
|
||||
payment.save()
|
||||
# TODO: run some custom code here
|
||||
|
||||
return HttpResponse(status=200)
|
||||
|
||||
|
||||
"""
|
||||
Django Stuff
|
||||
"""
|
||||
def useful_links(request):
|
||||
|
||||
|
||||
def useful_links(request):
|
||||
useful_links = UsefulLinks.objects.all()
|
||||
return render(request, "schasite/useful_links.html", {"links": useful_links})
|
||||
|
||||
def index(request):
|
||||
return render(request, "schasite/index.html", {})
|
||||
|
||||
def about_us(request):
|
||||
return render(request, "schasite/about_us.html", {})
|
||||
def index2(request):
|
||||
return render(request, "schasite/index2.html", {})
|
||||
|
||||
def calendar(request):
|
||||
|
||||
def about_us2(request):
|
||||
return render(request, "schasite/about_us2.html", {})
|
||||
|
||||
|
||||
def newsletters2(request):
|
||||
return render(request, "schasite/newsletters2.html", {})
|
||||
|
||||
|
||||
def calendar2(request):
|
||||
all_events = CalendarEvent.objects.all()
|
||||
future_events = [event if event.future_event() else None for event in all_events]
|
||||
past_events = [event if event.past_event() else None for event in all_events]
|
||||
# remove none for each list
|
||||
sanitized_future_events = [i for i in future_events if i is not None]
|
||||
sanitized_past_events = [i for i in past_events if i is not None]
|
||||
return render(request, "schasite/calendar.html", {'future_events': sanitized_future_events,
|
||||
'past_events': sanitized_past_events})
|
||||
return render(
|
||||
request,
|
||||
"schasite/calendar2.html",
|
||||
{
|
||||
"future_events": sanitized_future_events,
|
||||
"past_events": sanitized_past_events,
|
||||
},
|
||||
)
|
||||
|
||||
def newsletters(request):
|
||||
return render(request, "schasite/newsletters.html", {})
|
||||
|
||||
def membership_form(request):
|
||||
def scha_board2(request):
|
||||
def get_officer(position_name):
|
||||
try:
|
||||
return SCHAOfficer.objects.get(position=position_name)
|
||||
except:
|
||||
return None
|
||||
|
||||
officers = [
|
||||
get_officer("President"),
|
||||
get_officer("1st Vice President"),
|
||||
get_officer("2nd Vice President"),
|
||||
get_officer("Treasurer"),
|
||||
get_officer("Secretary"),
|
||||
get_officer("Website"),
|
||||
get_officer("Membership"),
|
||||
get_officer("Directory"),
|
||||
get_officer("Facebook"),
|
||||
get_officer("Eblasts"),
|
||||
]
|
||||
|
||||
return render(request, "schasite/scha_board2.html", {"officers": officers})
|
||||
|
||||
|
||||
def dues2(request):
|
||||
return render(request, "schasite/dues2.html", {})
|
||||
|
||||
|
||||
def membership_form2(request):
|
||||
def sanitize_phone_number(data):
|
||||
if len(data) > 0:
|
||||
data = data.replace('-','')
|
||||
if not data.startswith('+1'):
|
||||
data = '+1' + data
|
||||
|
||||
data = data.replace("-", "")
|
||||
if not data.startswith("+1"):
|
||||
data = "+1" + data
|
||||
|
||||
return data
|
||||
|
||||
if request.method == "POST":
|
||||
# before we pass in the data we want to sanitize the phone numbers
|
||||
post_data = request.POST.copy()
|
||||
post_data.update({'person1-phone_number':sanitize_phone_number(post_data['person1-phone_number'])})
|
||||
post_data.update({'person2-phone_number':sanitize_phone_number(post_data['person2-phone_number'])})
|
||||
post_data.update(
|
||||
{
|
||||
"person1-phone_number": sanitize_phone_number(
|
||||
post_data["person1-phone_number"]
|
||||
)
|
||||
}
|
||||
)
|
||||
post_data.update(
|
||||
{
|
||||
"person2-phone_number": sanitize_phone_number(
|
||||
post_data["person2-phone_number"]
|
||||
)
|
||||
}
|
||||
)
|
||||
membershipForm = ChildrenForm(post_data)
|
||||
addressForm = AddressForm(post_data)
|
||||
peopleForm1 = PeopleForm(post_data, prefix='person1')
|
||||
peopleForm2 = PeopleForm(post_data, prefix='person2')
|
||||
peopleForm1 = PeopleForm(post_data, prefix="person1")
|
||||
peopleForm2 = PeopleForm(post_data, prefix="person2")
|
||||
servicesForm = ServicesForm(post_data)
|
||||
committeeForm= CommitteeForm(post_data)
|
||||
committeeForm = CommitteeForm(post_data)
|
||||
# captchaForm = CaptchaForm(post_data)
|
||||
|
||||
|
||||
if membershipForm.is_valid() and addressForm.is_valid() and committeeForm.is_valid() and (peopleForm1.is_valid() or peopleForm2.is_valid()) and servicesForm.is_valid(): # and captchaForm.is_valid():
|
||||
|
||||
if (
|
||||
membershipForm.is_valid()
|
||||
and addressForm.is_valid()
|
||||
and committeeForm.is_valid()
|
||||
and (peopleForm1.is_valid() or peopleForm2.is_valid())
|
||||
and servicesForm.is_valid()
|
||||
): # and captchaForm.is_valid():
|
||||
with transaction.atomic():
|
||||
membershipForm = ChildrenForm({**post_data})
|
||||
membership = membershipForm.save(commit=False)
|
||||
membership.save()
|
||||
|
||||
|
||||
if peopleForm1.is_valid():
|
||||
people1_obj = peopleForm1.save(commit=False)
|
||||
people1_obj.membership = membership
|
||||
@@ -163,37 +235,183 @@ def membership_form(request):
|
||||
address_obj.membership = membership
|
||||
address_obj.save()
|
||||
|
||||
return redirect('index')
|
||||
return redirect("index")
|
||||
|
||||
|
||||
else:
|
||||
return render(request,
|
||||
"schasite/membership_form.html",
|
||||
{
|
||||
'membershipForm': ChildrenForm,
|
||||
'peopleForm1': peopleForm1,
|
||||
'peopleForm2': peopleForm2,
|
||||
'addressForm': addressForm,
|
||||
'committeeForm' : committeeForm,
|
||||
'servicesForm': servicesForm,
|
||||
# 'captchaForm': captchaForm,
|
||||
} )
|
||||
return render(
|
||||
request,
|
||||
"schasite/membership_form2.html",
|
||||
{
|
||||
"membershipForm": ChildrenForm,
|
||||
"peopleForm1": peopleForm1,
|
||||
"peopleForm2": peopleForm2,
|
||||
"addressForm": addressForm,
|
||||
"committeeForm": committeeForm,
|
||||
"servicesForm": servicesForm,
|
||||
# 'captchaForm': captchaForm,
|
||||
},
|
||||
)
|
||||
else:
|
||||
return render(request,
|
||||
"schasite/membership_form.html",
|
||||
{'membershipForm': ChildrenForm(),
|
||||
'peopleForm1': PeopleForm(prefix='person1'),
|
||||
'peopleForm2': PeopleForm(prefix='person2'),
|
||||
'committeeForm' : CommitteeForm(),
|
||||
'servicesForm': ServicesForm(),
|
||||
# 'captchaForm': CaptchaForm(),
|
||||
"addressForm": AddressForm(
|
||||
initial={
|
||||
"city": "Wheaton",
|
||||
"state": "IL",
|
||||
"zip_code": 60189,
|
||||
}
|
||||
)})
|
||||
return render(
|
||||
request,
|
||||
"schasite/membership_form2.html",
|
||||
{
|
||||
"membershipForm": ChildrenForm(),
|
||||
"peopleForm1": PeopleForm(prefix="person1"),
|
||||
"peopleForm2": PeopleForm(prefix="person2"),
|
||||
"committeeForm": CommitteeForm(),
|
||||
"servicesForm": ServicesForm(),
|
||||
# 'captchaForm': CaptchaForm(),
|
||||
"addressForm": AddressForm(
|
||||
initial={
|
||||
"city": "Wheaton",
|
||||
"state": "IL",
|
||||
"zip_code": 60189,
|
||||
}
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def useful_links2(request):
|
||||
useful_links = UsefulLinks.objects.all()
|
||||
return render(request, "schasite/useful_links2.html", {"links": useful_links})
|
||||
|
||||
|
||||
def index(request):
|
||||
return render(request, "schasite/index.html", {})
|
||||
|
||||
|
||||
def about_us(request):
|
||||
return render(request, "schasite/about_us.html", {})
|
||||
|
||||
|
||||
def calendar(request):
|
||||
all_events = CalendarEvent.objects.all()
|
||||
future_events = [event if event.future_event() else None for event in all_events]
|
||||
past_events = [event if event.past_event() else None for event in all_events]
|
||||
# remove none for each list
|
||||
sanitized_future_events = [i for i in future_events if i is not None]
|
||||
sanitized_past_events = [i for i in past_events if i is not None]
|
||||
return render(
|
||||
request,
|
||||
"schasite/calendar.html",
|
||||
{
|
||||
"future_events": sanitized_future_events,
|
||||
"past_events": sanitized_past_events,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def newsletters(request):
|
||||
return render(request, "schasite/newsletters.html", {})
|
||||
|
||||
|
||||
def membership_form(request):
|
||||
def sanitize_phone_number(data):
|
||||
if len(data) > 0:
|
||||
data = data.replace("-", "")
|
||||
if not data.startswith("+1"):
|
||||
data = "+1" + data
|
||||
|
||||
return data
|
||||
|
||||
if request.method == "POST":
|
||||
# before we pass in the data we want to sanitize the phone numbers
|
||||
post_data = request.POST.copy()
|
||||
post_data.update(
|
||||
{
|
||||
"person1-phone_number": sanitize_phone_number(
|
||||
post_data["person1-phone_number"]
|
||||
)
|
||||
}
|
||||
)
|
||||
post_data.update(
|
||||
{
|
||||
"person2-phone_number": sanitize_phone_number(
|
||||
post_data["person2-phone_number"]
|
||||
)
|
||||
}
|
||||
)
|
||||
membershipForm = ChildrenForm(post_data)
|
||||
addressForm = AddressForm(post_data)
|
||||
peopleForm1 = PeopleForm(post_data, prefix="person1")
|
||||
peopleForm2 = PeopleForm(post_data, prefix="person2")
|
||||
servicesForm = ServicesForm(post_data)
|
||||
committeeForm = CommitteeForm(post_data)
|
||||
# captchaForm = CaptchaForm(post_data)
|
||||
|
||||
if (
|
||||
membershipForm.is_valid()
|
||||
and addressForm.is_valid()
|
||||
and committeeForm.is_valid()
|
||||
and (peopleForm1.is_valid() or peopleForm2.is_valid())
|
||||
and servicesForm.is_valid()
|
||||
): # and captchaForm.is_valid():
|
||||
with transaction.atomic():
|
||||
membershipForm = ChildrenForm({**post_data})
|
||||
membership = membershipForm.save(commit=False)
|
||||
membership.save()
|
||||
|
||||
if peopleForm1.is_valid():
|
||||
people1_obj = peopleForm1.save(commit=False)
|
||||
people1_obj.membership = membership
|
||||
people1_obj.save()
|
||||
|
||||
if peopleForm2.is_valid():
|
||||
people2_obj = peopleForm2.save(commit=False)
|
||||
people2_obj.membership = membership
|
||||
people2_obj.save()
|
||||
|
||||
committee_obj = committeeForm.save(commit=False)
|
||||
committee_obj.membership = membership
|
||||
committee_obj.save()
|
||||
|
||||
services_obj = servicesForm.save(commit=False)
|
||||
services_obj.membership = membership
|
||||
services_obj.save()
|
||||
|
||||
address_obj = addressForm.save(commit=False)
|
||||
address_obj.membership = membership
|
||||
address_obj.save()
|
||||
|
||||
return redirect("index")
|
||||
|
||||
else:
|
||||
return render(
|
||||
request,
|
||||
"schasite/membership_form.html",
|
||||
{
|
||||
"membershipForm": ChildrenForm,
|
||||
"peopleForm1": peopleForm1,
|
||||
"peopleForm2": peopleForm2,
|
||||
"addressForm": addressForm,
|
||||
"committeeForm": committeeForm,
|
||||
"servicesForm": servicesForm,
|
||||
# 'captchaForm': captchaForm,
|
||||
},
|
||||
)
|
||||
else:
|
||||
return render(
|
||||
request,
|
||||
"schasite/membership_form.html",
|
||||
{
|
||||
"membershipForm": ChildrenForm(),
|
||||
"peopleForm1": PeopleForm(prefix="person1"),
|
||||
"peopleForm2": PeopleForm(prefix="person2"),
|
||||
"committeeForm": CommitteeForm(),
|
||||
"servicesForm": ServicesForm(),
|
||||
# 'captchaForm': CaptchaForm(),
|
||||
"addressForm": AddressForm(
|
||||
initial={
|
||||
"city": "Wheaton",
|
||||
"state": "IL",
|
||||
"zip_code": 60189,
|
||||
}
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def scha_board(request):
|
||||
return render(request, "schasite/scha_board.html", {})
|
||||
return render(request, "schasite/scha_board.html", {})
|
||||
|
||||
Reference in New Issue
Block a user