Compare commits

..

11 Commits

36 changed files with 2736 additions and 453 deletions

View File

@@ -53,7 +53,7 @@ MIDDLEWARE = [
] ]
ROOT_URLCONF = "scha.urls" ROOT_URLCONF = "scha.urls"
CSRF_TRUSTED_ORIGINS = ["https://schawheaton.aimloperations.com","https://www.schawheaton.aimloperations.com", "https://www.schawheaton.com", "https://schawheaton.com"]
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", "BACKEND": "django.template.backends.django.DjangoTemplates",

View File

@@ -24,6 +24,7 @@ from schasite.views import stripe_cancelled, stripe_success, stripe_webhook
urlpatterns = [ urlpatterns = [
path("schasite/", include("schasite.urls")), path("schasite/", include("schasite.urls")),
path("", include("schasite.urls")),
path("success/", stripe_success), path("success/", stripe_success),
path("cancelled/", stripe_cancelled), path("cancelled/", stripe_cancelled),
path("webhook/", stripe_webhook), path("webhook/", stripe_webhook),

View File

@@ -2,6 +2,7 @@ from django import forms
from django.forms import ModelForm from django.forms import ModelForm
from .models import ( from .models import (
Membership, Membership,
CommunityPost,
AddressModel1, AddressModel1,
MembershipPerson, MembershipPerson,
MembershipCommittee, MembershipCommittee,
@@ -10,21 +11,20 @@ from .models import (
from phonenumber_field.formfields import PhoneNumberField from phonenumber_field.formfields import PhoneNumberField
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
# from django_recaptcha.fields import ReCaptchaField from django_recaptcha.fields import ReCaptchaField
# from django.conf import settings from django.conf import settings
# from django_recaptcha.widgets import ReCaptchaV3 from django_recaptcha.widgets import ReCaptchaV3
# class CaptchaForm(forms.Form): class CaptchaForm(forms.Form):
# captcha = ReCaptchaField( captcha = ReCaptchaField(
# public_key=settings.RECAPTCHA_PUBLIC_KEY, public_key=settings.RECAPTCHA_PUBLIC_KEY,
# private_key=settings.RECAPTCHA_PRIVATE_KEY, private_key=settings.RECAPTCHA_PRIVATE_KEY,
# widget=ReCaptchaV3( widget=ReCaptchaV3(
# attrs={ attrs={
# 'required_score':0.85, 'required_score':0.85,
# } }
# ), ),
)
# )
class ChildrenForm(ModelForm): class ChildrenForm(ModelForm):
@@ -38,6 +38,10 @@ class AddressForm(ModelForm):
model = AddressModel1 model = AddressModel1
fields = ["address_1", "address_2", "city", "state", "zip_code"] fields = ["address_1", "address_2", "city", "state", "zip_code"]
class CommunityPostForm(ModelForm):
class Meta:
model = CommunityPost
fields = ['title','category','content']
class PeopleForm(ModelForm): class PeopleForm(ModelForm):
phone_number = PhoneNumberField(required=False) phone_number = PhoneNumberField(required=False)

View File

@@ -0,0 +1,290 @@
# Generated by Django 5.2 on 2025-04-21 17:42
import datetime
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("schasite", "0009_schaofficer_alter_payments_date"),
]
operations = [
migrations.CreateModel(
name="CommunityParks",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created", models.DateTimeField(default=django.utils.timezone.now)),
(
"last_modified",
models.DateTimeField(default=django.utils.timezone.now),
),
("name", models.CharField(max_length=255)),
("distance", models.DecimalField(decimal_places=1, max_digits=3)),
("description", models.CharField(max_length=1024)),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="CommunityPost",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created", models.DateTimeField(default=django.utils.timezone.now)),
(
"last_modified",
models.DateTimeField(default=django.utils.timezone.now),
),
("title", models.CharField(max_length=255)),
("category", models.CharField(max_length=255)),
("content", models.CharField(max_length=8192)),
("likes", models.IntegerField(default=0)),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="CommunitySchools",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created", models.DateTimeField(default=django.utils.timezone.now)),
(
"last_modified",
models.DateTimeField(default=django.utils.timezone.now),
),
("name", models.CharField(max_length=255)),
("name_school_url", models.URLField(max_length=256)),
("rating_url", models.URLField(max_length=256)),
("rating", models.DecimalField(decimal_places=2, max_digits=5)),
("distance", models.DecimalField(decimal_places=1, max_digits=3)),
("principal", models.CharField(max_length=255)),
("grades", models.CharField(max_length=255)),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="CommunityShoppingAndDining",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created", models.DateTimeField(default=django.utils.timezone.now)),
(
"last_modified",
models.DateTimeField(default=django.utils.timezone.now),
),
("name", models.CharField(max_length=255)),
("distance", models.DecimalField(decimal_places=1, max_digits=3)),
("description", models.CharField(max_length=1024)),
],
options={
"abstract": False,
},
),
migrations.AddField(
model_name="addressmodel1",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="addressmodel1",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="calendarevent",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="calendarevent",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="calendareventaddressmodel",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="calendareventaddressmodel",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="membership",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="membership",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="membershipcommittee",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="membershipcommittee",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="membershipperson",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="membershipperson",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="membershipservices",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="membershipservices",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="payments",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="payments",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="schaofficer",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="schaofficer",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="usefullinks",
name="created",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name="usefullinks",
name="last_modified",
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name="payments",
name="date",
field=models.DateField(
default=datetime.datetime(
2025, 4, 21, 17, 42, 47, 893058, tzinfo=datetime.timezone.utc
)
),
),
migrations.CreateModel(
name="CommunityComment",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created", models.DateTimeField(default=django.utils.timezone.now)),
(
"last_modified",
models.DateTimeField(default=django.utils.timezone.now),
),
("content", models.CharField(max_length=8192)),
("likes", models.IntegerField(default=0)),
(
"post",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="schasite.communitypost",
),
),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="CommunityPostReports",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created", models.DateTimeField(default=django.utils.timezone.now)),
(
"last_modified",
models.DateTimeField(default=django.utils.timezone.now),
),
(
"post",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="schasite.communitypost",
),
),
],
options={
"abstract": False,
},
),
]

View File

@@ -2,15 +2,31 @@ from django.db import models
from phonenumber_field.modelfields import PhoneNumberField from phonenumber_field.modelfields import PhoneNumberField
import datetime import datetime
from django.utils import timezone from django.utils import timezone
from django.conf import settings
class TimeInfoBase(models.Model):
created = models.DateTimeField(default=timezone.now)
last_modified = models.DateTimeField(default=timezone.now)
class Meta:
abstract = True
def save(self, *args, **kwargs):
if not kwargs.pop("skip_last_modified", False) and not hasattr(self, "skip_last_modified"):
self.last_modified = timezone.now()
if kwargs.get("update_fields") is not None:
kwargs["update_fields"] = list({*kwargs["update_fields"], "last_modified"})
super().save(*args, **kwargs)
# Create your models here. # Create your models here.
class UsefulLinks(models.Model): class UsefulLinks(TimeInfoBase):
name = models.CharField(max_length=256) name = models.CharField(max_length=256)
url = models.CharField(max_length=256) url = models.CharField(max_length=256)
class Membership(models.Model): class Membership(TimeInfoBase):
children = models.CharField(max_length=256, default="", blank=True, null=True) children = models.CharField(max_length=256, default="", blank=True, null=True)
def get_address_str(self): def get_address_str(self):
@@ -37,7 +53,7 @@ class Membership(models.Model):
return self.get_address_str() + " | " + self.get_person_1() return self.get_address_str() + " | " + self.get_person_1()
class AddressModel1(models.Model): class AddressModel1(TimeInfoBase):
membership = models.OneToOneField(Membership, on_delete=models.CASCADE) membership = models.OneToOneField(Membership, on_delete=models.CASCADE)
address_1 = models.CharField(max_length=128) address_1 = models.CharField(max_length=128)
address_2 = models.CharField(max_length=128, blank=True) address_2 = models.CharField(max_length=128, blank=True)
@@ -46,7 +62,7 @@ class AddressModel1(models.Model):
zip_code = models.CharField(max_length=5) zip_code = models.CharField(max_length=5)
class CalendarEvent(models.Model): class CalendarEvent(TimeInfoBase):
event_name = models.CharField(max_length=256) event_name = models.CharField(max_length=256)
start_date = models.DateField(blank=True, null=True) start_date = models.DateField(blank=True, null=True)
end_date = models.DateField(blank=True, null=True) end_date = models.DateField(blank=True, null=True)
@@ -54,7 +70,7 @@ class CalendarEvent(models.Model):
coordinator_email = models.EmailField(max_length=256, blank=True, null=True) coordinator_email = models.EmailField(max_length=256, blank=True, null=True)
event_link_name = models.CharField(max_length=64, 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) event_url = models.URLField(max_length=256, blank=True, null=True)
description= models.CharField(max_length=1024, default="") #description= models.CharField(max_length=1024, default="")
def has_date(self): def has_date(self):
return not self.start_date is None return not self.start_date is None
@@ -75,7 +91,7 @@ class CalendarEvent(models.Model):
return not self.has_date() return not self.has_date()
class CalendarEventAddressModel(models.Model): class CalendarEventAddressModel(TimeInfoBase):
calendar_event = models.OneToOneField(CalendarEvent, on_delete=models.CASCADE) calendar_event = models.OneToOneField(CalendarEvent, on_delete=models.CASCADE)
address_1 = models.CharField(max_length=128) address_1 = models.CharField(max_length=128)
address_2 = models.CharField(max_length=128, blank=True) address_2 = models.CharField(max_length=128, blank=True)
@@ -89,7 +105,7 @@ class CalendarEventAddressModel(models.Model):
) )
class MembershipPerson(models.Model): class MembershipPerson(TimeInfoBase):
membership = models.ForeignKey( membership = models.ForeignKey(
Membership, on_delete=models.CASCADE, blank=True, null=True Membership, on_delete=models.CASCADE, blank=True, null=True
) )
@@ -102,7 +118,7 @@ class MembershipPerson(models.Model):
return self.email if self.email else "No email" return self.email if self.email else "No email"
class MembershipCommittee(models.Model): class MembershipCommittee(TimeInfoBase):
membership = models.OneToOneField(Membership, on_delete=models.CASCADE) membership = models.OneToOneField(Membership, on_delete=models.CASCADE)
block_captain = models.BooleanField(default=False, blank=True, null=True) block_captain = models.BooleanField(default=False, blank=True, null=True)
coordinator = models.BooleanField(default=False, blank=True, null=True) coordinator = models.BooleanField(default=False, blank=True, null=True)
@@ -119,7 +135,7 @@ class MembershipCommittee(models.Model):
no_preference = models.BooleanField(default=False, blank=True, null=True) no_preference = models.BooleanField(default=False, blank=True, null=True)
class MembershipServices(models.Model): class MembershipServices(TimeInfoBase):
membership = models.OneToOneField(Membership, on_delete=models.CASCADE) membership = models.OneToOneField(Membership, on_delete=models.CASCADE)
babysitting = models.BooleanField(default=False, blank=True, null=True) babysitting = models.BooleanField(default=False, blank=True, null=True)
lawn_mowing = models.BooleanField(default=False, blank=True, null=True) lawn_mowing = models.BooleanField(default=False, blank=True, null=True)
@@ -131,7 +147,7 @@ class MembershipServices(models.Model):
other_desc = models.CharField(default="", blank=True, null=True, max_length=256) other_desc = models.CharField(default="", blank=True, null=True, max_length=256)
class Payments(models.Model): class Payments(TimeInfoBase):
date = models.DateField(default=timezone.now()) 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) email = models.EmailField(blank=True, null=True)
@@ -140,26 +156,58 @@ class Payments(models.Model):
) )
class SCHAOfficer(models.Model): class SCHAOfficer(TimeInfoBase):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
position = models.CharField(max_length=255) position = models.CharField(max_length=255)
email = models.EmailField(max_length=255) email = models.EmailField(max_length=255)
# class CommunitySchools(models.Model): ### NOT USED YET ###
# 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): class CommunitySchools(TimeInfoBase):
# name = models.CharField(max_length=255) name = models.CharField(max_length=255)
# distance = models.DecimalField(max_digits=3, decimal_places=1) name_school_url = models.URLField(max_length=256)
# description = models.CharField(max_length=1024) 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 CommunityParks(models.Model): class CommunityShoppingAndDining(TimeInfoBase):
# name = models.CharField(max_length=255) name = models.CharField(max_length=255)
# distance = models.DecimalField(max_digits=3, decimal_places=1) distance = models.DecimalField(max_digits=3, decimal_places=1)
# description = models.CharField(max_length=1024) description = models.CharField(max_length=1024)
class CommunityParks(TimeInfoBase):
name = models.CharField(max_length=255)
distance = models.DecimalField(max_digits=3, decimal_places=1)
description = models.CharField(max_length=1024)
### Member only models
class CommunityMember(TimeInfoBase):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
membership_person = models.OneToOneField(MembershipPerson, on_delete=models.CASCADE)
class CommunityPost(TimeInfoBase):
title = models.CharField(max_length=255)
category = models.CharField(max_length=255)
content = models.CharField(max_length=1024*8)
likes = models.IntegerField(default=0)
author = models.OneToOneField(CommunityMember, on_delete=models.CASCADE)
class CommunityPostReports(TimeInfoBase):
# for anyone who reports a post
post = models.ForeignKey(CommunityPost, on_delete=models.CASCADE)
reporter = models.OneToOneField(CommunityMember, on_delete=models.CASCADE)
class CommunityPostLikes(TimeInfoBase):
# for anyone who likes a post
post = models.ForeignKey(CommunityPost, on_delete=models.CASCADE)
reporter = models.OneToOneField(CommunityMember, on_delete=models.CASCADE)
class CommunityComment(TimeInfoBase):
post = models.ForeignKey(CommunityPost, on_delete=models.CASCADE)
content = models.CharField(max_length=1024*8)
likes = models.IntegerField(default=0)
author = models.OneToOneField(CommunityMember, on_delete=models.CASCADE)

View File

@@ -4,7 +4,6 @@
} }
body { body {
padding-top: 56px;
} }
section { section {

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -11,7 +11,7 @@
<div class="container"> <div class="container">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-lg-6 mb-4 mb-lg-0"> <div class="col-lg-6 mb-4 mb-lg-0">
<img src="../images/schools.jpg" alt="Local Schools" class="img-fluid rounded shadow"> <img src="{% static 'images/District_200.png' %}" alt="Local Schools" class="img-fluid rounded shadow">
</div> </div>
<div class="col-lg-6"> <div class="col-lg-6">
<h2 class="text-success mb-4">Excellent Schools Serving Our Community</h2> <h2 class="text-success mb-4">Excellent Schools Serving Our Community</h2>
@@ -95,7 +95,7 @@
<strong>Grades:</strong> 9-12<br> <strong>Grades:</strong> 9-12<br>
<strong>Enrollment:</strong> 698<br> <strong>Enrollment:</strong> 698<br>
<strong>Distance:</strong> 2.7 miles from community entrance<br> <strong>Distance:</strong> 2.7 miles from community entrance<br>
<strong>Principal:</strong> TBD</p> <strong>Principal:</strong> Raeann Huhn </p>
<a href="https://www.sfhscollegeprep.org/" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a> <a href="https://www.sfhscollegeprep.org/" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a>
</div> </div>
</div> </div>
@@ -111,7 +111,7 @@
<strong>Grades:</strong> 9-12<br> <strong>Grades:</strong> 9-12<br>
<strong>Enrollment:</strong> 1265<br> <strong>Enrollment:</strong> 1265<br>
<strong>Distance:</strong> 5.9 miles from community entrance<br> <strong>Distance:</strong> 5.9 miles from community entrance<br>
<strong>Principal:</strong> TBD</p> <strong>Principal:</strong> Sheri Costello </p>
<a href="http://www.cusd200.org/Domain/140" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a> <a href="http://www.cusd200.org/Domain/140" target="_blank" class="btn btn-sm btn-outline-success">Visit Website</a>
</div> </div>
</div> </div>
@@ -127,7 +127,7 @@
<div class="container"> <div class="container">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-lg-6 order-lg-2 mb-4 mb-lg-0"> <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"> <img src="{% static 'images/header.gif' %}" alt="Community History" class="img-fluid rounded shadow">
</div> </div>
<div class="col-lg-6 order-lg-1"> <div class="col-lg-6 order-lg-1">
<h2 class="text-success mb-4">Our Rich History</h2> <h2 class="text-success mb-4">Our Rich History</h2>
@@ -161,7 +161,7 @@
<div class="row g-4"> <div class="row g-4">
<div class="col-md-6"> <div class="col-md-6">
<div class="card h-100 shadow-sm"> <div class="card h-100 shadow-sm">
<img src="../images/greenwood-mall.jpg" class="card-img-top" alt="Greenwood Mall"> <img src="{% static 'images/danada.jpeg' %}" class="card-img-top" alt="Greenwood Mall">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Danada</h5> <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"><i class="bi bi-geo-alt-fill text-success me-2"></i>1.0 miles from community</p>
@@ -174,7 +174,7 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="card h-100 shadow-sm"> <div class="card h-100 shadow-sm">
<img src="../images/farmers-market.jpg" class="card-img-top" alt="Farmers Market"> <img src="{% static 'images/downtown_wheaton.jpeg' %}" class="card-img-top" alt="Farmers Market">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Downtown Wheaton</h5> <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"><i class="bi bi-geo-alt-fill text-success me-2"></i>2.7 miles from community</p>
@@ -188,7 +188,7 @@
<div class="col-md-6"> <div class="col-md-6">
<div class="card h-100 shadow-sm"> <div class="card h-100 shadow-sm">
<img src="../images/farmers-market.jpg" class="card-img-top" alt="Farmers Market"> <img src="{% static 'images/wheaton_farmers_market.jpeg' %}" class="card-img-top" alt="Farmers Market">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Wheaton Farmersmarket</h5> <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"><i class="bi bi-geo-alt-fill text-success me-2"></i>2.8 miles from community</p>
@@ -201,7 +201,7 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="card h-100 shadow-sm"> <div class="card h-100 shadow-sm">
<img src="../images/dining-district.jpg" class="card-img-top" alt="Dining District"> <img src="{% static 'images/downtown_naperville.jpeg' %}" class="card-img-top" alt="Dining District">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Downtown Naperville</h5> <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"><i class="bi bi-geo-alt-fill text-success me-2"></i>6.2 miles from community</p>
@@ -227,7 +227,7 @@
<div class="card shadow-sm h-100"> <div class="card shadow-sm h-100">
<div class="row g-0 h-100"> <div class="row g-0 h-100">
<div class="col-md-5"> <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;"> <img src="{% static 'images/brighton.jpg' %}" class="img-fluid rounded-start h-100" alt="Community Park" style="object-fit: cover;">
</div> </div>
<div class="col-md-7"> <div class="col-md-7">
<div class="card-body"> <div class="card-body">
@@ -249,7 +249,7 @@
<div class="card shadow-sm h-100"> <div class="card shadow-sm h-100">
<div class="row g-0 h-100"> <div class="row g-0 h-100">
<div class="col-md-5"> <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;"> <img src="{% static 'images/seven_gables.jpg' %}" class="img-fluid rounded-start h-100" alt="Nature Preserve" style="object-fit: cover;">
</div> </div>
<div class="col-md-7"> <div class="col-md-7">
<div class="card-body"> <div class="card-body">
@@ -260,8 +260,8 @@
<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>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>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>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>Basketball Court</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>Picnic Tables</li>
<li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Dogs Allowed</li> <li class="list-group-item"><i class="bi bi-check text-success me-2"></i>Dogs Allowed</li>
</ul> </ul>
<p class="card-text"><small class="text-muted">Open daily from 8am to sunset</small></p> <p class="card-text"><small class="text-muted">Open daily from 8am to sunset</small></p>

View File

@@ -0,0 +1,174 @@
<!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" />
<script async defer src="https://tianji.aimloperations.com/tracker.js" data-website-id="cm9qziuid9vr7v6dtdbnx8406"></script>
<script type="text/css">
.bg-light {
background-color: #f8f9fa!important;
}
.auth-card {
max-width: 500px;
margin: 0 auto;
}
/* Member Portal */
.member-navbar {
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.member-navbar .nav-link {
padding: 0.5rem 1rem;
}
/* Directory */
.directory-table th {
white-space: nowrap;
}
/* Posts */
.post-card {
transition: transform 0.2s ease;
}
.post-card:hover {
transform: translateY(-2px);
}
.post-avatar {
width: 40px;
height: 40px;
}
/* Responsive adjustments */
@media (max-width: 767.98px) {
.member-navbar .navbar-nav {
margin-top: 1rem;
}
.directory-table {
font-size: 0.875rem;
}
}
</script>
</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 me-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'dashboard' %}"><i class="bi bi-speedometer2 me-1"></i>Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="{% url 'directory' %}"><i class="bi bi-people me-1"></i>Directory</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'posts' %}"><i class="bi bi-chat-square-text me-1"></i>Community Posts</a>
</li>
</ul>
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown">
<i class="bi bi-person-circle me-1"></i>John D.
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#"><i class="bi bi-person me-2"></i>Profile</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-gear me-2"></i>Settings</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="../../auth/login.html"><i class="bi bi-box-arrow-right me-2"></i>Logout</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
{% block content %}
{% endblock %}
<!-- Footer -->
<footer class="bg-dark text-white pt-4 pb-3">
<div class="container">
<div class="row">
<div class="col-md-6">
<h5 class="text-uppercase mb-4">Stonehedge Community Homeowners Association</h5>
<p class="text-white-50">1900 Spring Rd. #200<br>Oak Brook, IL 60523</p>
</div>
<div class="col-md-3">
<h5 class="text-uppercase">Quick Links</h5>
<ul class="list-unstyled">
<li><a href="index.html" class="text-white-50">Dashboard</a></li>
<li><a href="directory.html" class="text-white-50">Directory</a></li>
<li><a href="posts/" class="text-white-50">Community Posts</a></li>
</ul>
</div>
<div class="col-md-3">
<h5 class="text-uppercase">Account</h5>
<ul class="list-unstyled">
<li><a href="#" class="text-white-50">Profile</a></li>
<li><a href="#" class="text-white-50">Settings</a></li>
<li><a href="../../auth/login.html" class="text-white-50">Logout</a></li>
</ul>
</div>
</div>
<hr class="my-3">
<div class="row">
<div class="col-12 text-center">
<p class="mb-0 small">&copy; 2023 Greenwood Estates HOA. All rights reserved.</p>
</div>
</div>
</div>
</footer>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<!-- Directory Script -->
<script>
document.addEventListener('DOMContentLoaded', function() {
// Search functionality
const searchInput = document.getElementById('directorySearch');
const tableRows = document.querySelectorAll('tbody tr');
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
tableRows.forEach(row => {
const rowText = row.textContent.toLowerCase();
if (rowText.includes(searchTerm)) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
});
// Filter functionality
const filterSelect = document.getElementById('directoryFilter');
filterSelect.addEventListener('change', function() {
const filterValue = this.value;
// In a real implementation, this would filter the directory
console.log('Filter by:', filterValue);
});
});
</script>
</body>
</html>

View File

@@ -12,8 +12,11 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<!-- Custom CSS --> <!-- Custom CSS -->
<link href="{% static 'css/style2.css' %}" rel="stylesheet" type="text/css" /> <link href="{% static 'css/style2.css' %}" rel="stylesheet" type="text/css" />
<script async defer src="https://tianji.aimloperations.com/tracker.js" data-website-id="cm9qziuid9vr7v6dtdbnx8406"></script>
</head> </head>
<body> <body>
<!-- Navigation --> <!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-success sticky-top"> <nav class="navbar navbar-expand-lg navbar-dark bg-success sticky-top">
<div class="container"> <div class="container">
@@ -59,7 +62,6 @@
<div class="row"> <div class="row">
<div class="col-lg-6 mb-4 mb-lg-0"> <div class="col-lg-6 mb-4 mb-lg-0">
<h5 class="text-uppercase mb-4">Stonehedge Community Homeowners Association</h5> <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>
<div class="col-lg-4 offset-lg-2 mb-4 mb-lg-0"> <div class="col-lg-4 offset-lg-2 mb-4 mb-lg-0">
<h5 class="text-uppercase mb-4">Quick Links</h5> <h5 class="text-uppercase mb-4">Quick Links</h5>
@@ -76,7 +78,9 @@
</div> </div>
<hr class="my-4 text-white-50"> <hr class="my-4 text-white-50">
<div class="row align-items-center"> <div class="row align-items-center">
<div id="footer">&copy; Stonehedge Community Homeowners Association 2010-<script>document.write( new Date().getFullYear() );</script>. All rights reserved <div id="footer">
<p>&copy; Stonehedge Community Homeowners Association 2010-<script>document.write( new Date().getFullYear() );</script>. All rights reserved </p>
<p> Developed by <a href="https://aimloperations.com">AI ML Operations, LLC</a></p>
</div> </div>
</div> </div>
</footer> </footer>
@@ -87,4 +91,4 @@
{% block extra_js %} {% block extra_js %}
{% endblock %} {% endblock %}
</body> </body>
</html> </html>

View File

@@ -109,9 +109,8 @@
<i class="bi bi-credit-card text-success display-4 mb-3"></i> <i class="bi bi-credit-card text-success display-4 mb-3"></i>
<h4 class="text-success">Credit/Debit Card</h4> <h4 class="text-success">Credit/Debit Card</h4>
<p class="card-text">Pay securely with Visa, Mastercard, American Express, or Discover.</p> <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 <button class="btn btn-success mt-3" id="submitBtn">Pay Dues</button>
</a>
</div> </div>
</div> </div>
</div> </div>
@@ -192,39 +191,6 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<!-- Stripe Integration Script (example) --> <!-- Stripe Integration Script (example) -->
<script> <script src="https://js.stripe.com/v3/"></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> <script src="{% static 'main.js' %}"></script>
{% endblock %} {% endblock %}

View File

@@ -1,4 +1,4 @@
{% extends 'schasite/base.html' %} {% extends 'schasite/base2.html' %}
{% load static %} {% load static %}
{% block pagetitle %} {% block pagetitle %}

View File

@@ -1,4 +1,4 @@
{% extends 'schasite/base.html' %} {% extends 'schasite/base2.html' %}
{% load static %} {% load static %}
{% block pagetitle %} {% block pagetitle %}

View File

@@ -3,21 +3,42 @@
{% block pagetitle %} {% block pagetitle %}
<title>Stonehedge Community Homeowners Association</title> <title>Stonehedge Community Homeowners Association</title>
<style type="text/css">
.hero-section {
background: url({% static 'images/header.gif' %});
height: 60vh;
display: flex;
background-repeat:no-repeat;
background-position: center center;
align-items: center;
justify-content: center;
flex-direction: column;
color: white;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
</style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<!-- Hero Section --> <!-- Hero Section -->
<section id="home" class="py-5 bg-success bg-opacity-10"> <section id="home" class="py-5 bg-success bg-opacity-10">
<div class="container py-5"> <div class="container py-5">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-lg-8 mb-4 mb-lg-0"> <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>
<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> <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> <a href="{% url 'about_us2' %}" class="btn btn-success btn-lg px-4">Learn More</a>
</div>
<div class="col-lg-4"> </div>
<img src='{% static "images/bricks.jpg" %}' alt="Stonehedge Community Homeowners Association" class="img-fluid rounded-circle shadow">
</div>
<!-- <div class="col-lg-4">
<img src="{% static 'images/header.gif' %}" alt="Stonehedge Community Homeowners Association" class="img-fluid rounded-circle shadow">
</div> -->
</div> </div>
</div> </div>
</section> </section>
@@ -27,112 +48,39 @@
<div class="container"> <div class="container">
<h2 class="text-center text-success mb-5">About Our Community</h2> <h2 class="text-center text-success mb-5">About Our Community</h2>
<div class="row g-4"> <div class="row g-4">
<div class="col-md-4">
<div class="col-md-6">
<div class="card h-100 shadow-sm"> <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"> <div class="card-body">
<h5 class="card-title">Amenities</h5> <h5 class="card-title"><a href="{% url 'dues2' %}">Pay Dues</a></h5>
<p class="card-text">Our community features a swimming pool, playground, walking trails, and clubhouse available for resident use.</p> <p class="card-text">Make sure to pay your dues</p>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-6">
<div class="card h-100 shadow-sm"> <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"> <div class="card-body">
<h5 class="card-title">HOA Board</h5> <h5 class="card-title"><a href="{% url 'membership_form2' %}">Join</a></h5>
<p class="card-text">Fill out the membership form and be added to the mailing list</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100 shadow-sm">
<div class="card-body">
<h5 class="card-title"><a href="{% url 'calendar2' %}">Events</a></h5>
<p class="card-text">Checkout out the upcoming and past events!</p>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card h-100 shadow-sm">
<div class="card-body">
<h5 class="card-title"><a href="{% url 'scha_board2' %}">HOA Board</a></h5>
<p class="card-text">Meet our elected board members who volunteer their time to maintain and improve our neighborhood.</p> <p class="card-text">Meet our elected board members who volunteer their time to maintain and improve our neighborhood.</p>
</div> </div>
</div> </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>
</div> </div>
</section> </section>

View File

@@ -0,0 +1,123 @@
{% extends 'schasite/authenticated_base.html' %}
{% load static %}
{% block pagetitle %}
<title>Dashboard | SCHA</title>
{% endblock %}
{% block content %}
<main class="py-5">
<div class="container">
<div class="row mb-4">
<div class="col-12">
<h1 class="display-6">Member Dashboard</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item active" aria-current="page">Home</li>
</ol>
</nav>
</div>
</div>
<div class="row g-4">
<!-- Welcome Card -->
<div class="col-md-6">
<div class="card shadow-sm h-100">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-house-door me-2"></i>Welcome</h3>
</div>
<div class="card-body">
<h4 class="card-title">Hello, John Doe!</h4>
<p class="card-text">Welcome to your Greenwood Estates member portal. From here you can access community resources, connect with neighbors, and stay informed.</p>
<div class="alert alert-success">
<i class="bi bi-info-circle-fill me-2"></i>
Your membership is active through December 31, 2024.
</div>
</div>
</div>
</div>
<!-- Quick Links -->
<div class="col-md-6">
<div class="card shadow-sm h-100">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-lightning me-2"></i>Quick Actions</h3>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-sm-6">
<a href="{% url 'directory' %}" class="btn btn-outline-success w-100 py-3">
<i class="bi bi-people display-6 d-block mb-2"></i>
Member Directory
</a>
</div>
<div class="col-sm-6">
<a href="{% url 'posts' %}" class="btn btn-outline-success w-100 py-3">
<i class="bi bi-chat-square-text display-6 d-block mb-2"></i>
Community Posts
</a>
</div>
<div class="col-sm-6">
<a href="{% url 'posts_create' %}" class="btn btn-outline-success w-100 py-3">
<i class="bi bi-plus-circle display-6 d-block mb-2"></i>
Create Post
</a>
</div>
<!-- <div class="col-sm-6">
<a href="#" class="btn btn-outline-success w-100 py-3">
<i class="bi bi-calendar-event display-6 d-block mb-2"></i>
View Calendar
</a>
</div> -->
</div>
</div>
</div>
</div>
<!-- Recent Activity -->
<div class="col-12">
<div class="card shadow-sm">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-activity me-2"></i>Your Recently Updated Posts</h3>
</div>
<div class="card-body">
<div class="list-group">
{% for recent_post in member_recent_posts %}
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{ recent_post.title }}</h5>
<small>3 days ago</small>
</div>
<p class="mb-1">{{ recent_post.content }}</p>
<small>Posted by Sarah Johnson</small>
</a>
{% endfor %}
</div>
</div>
</div>
<div class="card shadow-sm">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-activity me-2"></i>Recent Community Posts</h3>
</div>
<div class="card-body">
<div class="list-group">
{% for recent_post in recent_posts %}
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{ recent_post.title }}</h5>
<small>3 days ago</small>
</div>
<p class="mb-1">{{ recent_post.content }}</p>
<small>Posted by Sarah Johnson</small>
</a>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
</main>
{% endblock %}
{% block extra_js %}
{% endblock %}

View File

@@ -0,0 +1,229 @@
{% extends 'schasite/authenticated_base.html' %}
{% load static %}
{% block pagetitle %}
<title>Directory | SCHA</title>
{% endblock %}
{% block content %}
<main class="py-5">
<div class="container">
<div class="row mb-4">
<div class="col-12">
<h1 class="display-6">Member Directory</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Directory</li>
</ol>
</nav>
</div>
</div>
<div class="row mb-4">
<div class="col-md-6 mb-3 mb-md-0">
<div class="input-group">
<span class="input-group-text"><i class="bi bi-search"></i></span>
<input type="text" class="form-control" placeholder="Search members..." id="directorySearch">
</div>
</div>
<div class="col-md-6">
<div class="input-group">
<span class="input-group-text">Filter</span>
<select class="form-select" id="directoryFilter">
<option selected>All Members</option>
<option>Committee Members</option>
<option>Board Members</option>
<option>By Street</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card 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"><i class="bi bi-people-fill me-2"></i>Community Members</h3>
<span class="badge bg-light text-success">142 Members</span>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-success">
<tr>
<th>Name</th>
<th>Address</th>
<th>Contact</th>
<th>Committees</th>
<th>Services</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-0">John & Sarah Smith</h6>
<small class="text-muted">Lot #42</small>
</div>
</div>
</td>
<td>123 Oak Drive</td>
<td>
<a href="mailto:john.smith@example.com" class="text-decoration-none"><i class="bi bi-envelope me-1"></i>Email</a><br>
<a href="tel:5551234567" class="text-decoration-none"><i class="bi bi-telephone me-1"></i>555-123-4567</a>
</td>
<td>
<span class="badge bg-success bg-opacity-10 text-success mb-1">Social Committee</span>
<span class="badge bg-success bg-opacity-10 text-success">Welcoming</span>
</td>
<td>
<span class="badge bg-info bg-opacity-10 text-info">Pet Care</span>
</td>
</tr>
<tr>
<td>
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-0">Robert Wilson</h6>
<small class="text-muted">Lot #18</small>
</div>
</div>
</td>
<td>456 Maple Street</td>
<td>
<a href="mailto:rwilson@example.com" class="text-decoration-none"><i class="bi bi-envelope me-1"></i>Email</a><br>
<a href="tel:5559876543" class="text-decoration-none"><i class="bi bi-telephone me-1"></i>555-987-6543</a>
</td>
<td>
<span class="badge bg-success bg-opacity-10 text-success mb-1">Architectural</span>
<span class="badge bg-success bg-opacity-10 text-success">Board</span>
</td>
<td>
<span class="badge bg-info bg-opacity-10 text-info">Lawn Mowing</span>
<span class="badge bg-info bg-opacity-10 text-info">Snow Shoveling</span>
</td>
</tr>
<tr>
<td>
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-0">Emily Davis</h6>
<small class="text-muted">Lot #75</small>
</div>
</div>
</td>
<td>789 Pine Road</td>
<td>
<a href="mailto:emily.d@example.com" class="text-decoration-none"><i class="bi bi-envelope me-1"></i>Email</a><br>
<a href="tel:5554567890" class="text-decoration-none"><i class="bi bi-telephone me-1"></i>555-456-7890</a>
</td>
<td>
<span class="badge bg-success bg-opacity-10 text-success mb-1">Communications</span>
</td>
<td>
<span class="badge bg-info bg-opacity-10 text-info">Babysitting</span>
<span class="badge bg-info bg-opacity-10 text-info">House Sitting</span>
</td>
</tr>
<tr>
<td>
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-0">Michael & Lisa Brown</h6>
<small class="text-muted">Lot #33</small>
</div>
</div>
</td>
<td>321 Elm Avenue</td>
<td>
<a href="mailto:mbrown@example.com" class="text-decoration-none"><i class="bi bi-envelope me-1"></i>Email</a><br>
<a href="tel:5557890123" class="text-decoration-none"><i class="bi bi-telephone me-1"></i>555-789-0123</a>
</td>
<td>
<span class="badge bg-success bg-opacity-10 text-success mb-1">Landscape</span>
<span class="badge bg-success bg-opacity-10 text-success">Safety</span>
</td>
<td>
<span class="badge bg-info bg-opacity-10 text-info">Other (Handyman)</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="card-footer">
<nav aria-label="Directory navigation">
<ul class="pagination justify-content-center mb-0">
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Previous</a>
</li>
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#">Next</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</main>
{% endblock %}
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Search functionality
const searchInput = document.getElementById('directorySearch');
const tableRows = document.querySelectorAll('tbody tr');
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
tableRows.forEach(row => {
const rowText = row.textContent.toLowerCase();
if (rowText.includes(searchTerm)) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
});
// Filter functionality
const filterSelect = document.getElementById('directoryFilter');
filterSelect.addEventListener('change', function() {
const filterValue = this.value;
// In a real implementation, this would filter the directory
console.log('Filter by:', filterValue);
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,166 @@
{% extends 'schasite/authenticated_base.html' %}
{% load static %}
{% block pagetitle %}
<title>Posts | SCHA</title>
{% endblock %}
{% block content %}
<main class="py-5">
<div class="container">
<div class="row mb-4">
<div class="col-12">
<h1 class="display-6">Community Posts</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Community Posts</li>
</ol>
</nav>
</div>
</div>
<div class="row mb-4">
<div class="col-md-8 mb-3 mb-md-0">
<div class="input-group">
<span class="input-group-text"><i class="bi bi-search"></i></span>
<input type="text" class="form-control" placeholder="Search posts...">
<button class="btn btn-success" type="button">Search</button>
</div>
</div>
<div class="col-md-4 text-md-end">
<a href="{% url 'posts_create' %}" class="btn btn-success">
<i class="bi bi-plus-circle me-2"></i>New Post
</a>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="card shadow-sm mb-4">
<div class="card-header bg-success text-white">
<div class="d-flex justify-content-between align-items-center">
<h3 class="h5 mb-0"><i class="bi bi-pin-angle me-2"></i>Pinned Post</h3>
<span class="badge bg-light text-success">Announcement</span>
</div>
</div>
<div class="card-body">
<div class="d-flex mb-3">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h5 class="mb-1">Annual Community Picnic</h5>
<div class="d-flex flex-wrap text-muted small mb-2">
<div class="me-3"><i class="bi bi-person me-1"></i>Sarah Johnson</div>
<div class="me-3"><i class="bi bi-calendar me-1"></i>June 5, 2023</div>
<div><i class="bi bi-chat me-1"></i>12 comments</div>
</div>
</div>
</div>
<p class="card-text">Our annual community picnic is scheduled for Saturday, June 15th from 11am-3pm at the Greenwood Park. This year we'll have a bounce house, face painting, and a pie baking contest. Please RSVP by June 10th so we can plan for food quantities.</p>
<div class="mt-3">
<a href="#" class="btn btn-sm btn-outline-success me-2"><i class="bi bi-hand-thumbs-up me-1"></i>Like (24)</a>
<a href="#" class="btn btn-sm btn-outline-success"><i class="bi bi-chat me-1"></i>Comment</a>
</div>
</div>
</div>
{% for post in posts %}
<div class="card shadow-sm mb-4">
<div class="card-body">
<div class="d-flex mb-3">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h5 class="mb-1"><a href="{% url 'posts_detail' post_id=post.id %}">{{ post.title }}</a></h5>
<div class="d-flex flex-wrap text-muted small mb-2">
<div class="me-3"><i class="bi bi-person me-1"></i>Emily Davis</div>
<div class="me-3"><i class="bi bi-calendar me-1"></i>June 3, 2023</div>
<div><i class="bi bi-chat me-1"></i>8 comments</div>
</div>
</div>
</div>
<p class="card-text">{{ post.content }}</p>
<div class="mt-3">
<a href="#" class="btn btn-sm btn-outline-success me-2"><i class="bi bi-hand-thumbs-up me-1"></i>Like (15)</a>
<a href="#" class="btn btn-sm btn-outline-success"><i class="bi bi-chat me-1"></i>Comment</a>
</div>
</div>
</div>
{% endfor %}
<div class="card shadow-sm mb-4">
<div class="card-body">
<div class="d-flex mb-3">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h5 class="mb-1">Lost Dog - Please Help!</h5>
<div class="d-flex flex-wrap text-muted small mb-2">
<div class="me-3"><i class="bi bi-person me-1"></i>Emily Davis</div>
<div class="me-3"><i class="bi bi-calendar me-1"></i>June 3, 2023</div>
<div><i class="bi bi-chat me-1"></i>8 comments</div>
</div>
</div>
</div>
<p class="card-text">Our golden retriever Max got out of our yard this morning around 9am. He was last seen near the corner of Oak Drive and Maple Street. He's friendly, wearing a blue collar with our contact info. Please call me at 555-456-7890 if you see him!</p>
<div class="mt-3">
<a href="#" class="btn btn-sm btn-outline-success me-2"><i class="bi bi-hand-thumbs-up me-1"></i>Like (15)</a>
<a href="#" class="btn btn-sm btn-outline-success"><i class="bi bi-chat me-1"></i>Comment</a>
</div>
</div>
</div>
<div class="card shadow-sm mb-4">
<div class="card-body">
<div class="d-flex mb-3">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h5 class="mb-1">Yard Sale This Weekend</h5>
<div class="d-flex flex-wrap text-muted small mb-2">
<div class="me-3"><i class="bi bi-person me-1"></i>Michael Brown</div>
<div class="me-3"><i class="bi bi-calendar me-1"></i>June 1, 2023</div>
<div><i class="bi bi-chat me-1"></i>5 comments</div>
</div>
</div>
</div>
<p class="card-text">We're having a yard sale at 321 Elm Avenue this Saturday from 8am-2pm. Lots of household items, kids' clothes, and furniture. Everything must go!</p>
<div class="mt-3">
<a href="#" class="btn btn-sm btn-outline-success me-2"><i class="bi bi-hand-thumbs-up me-1"></i>Like (7)</a>
<a href="#" class="btn btn-sm btn-outline-success"><i class="bi bi-chat me-1"></i>Comment</a>
</div>
</div>
</div>
<nav aria-label="Posts navigation">
<ul class="pagination justify-content-center">
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1">Previous</a>
</li>
<li class="page-item active"><a class="page-link" href="#">1</a></li>
<li class="page-item"><a class="page-link" href="#">2</a></li>
<li class="page-item"><a class="page-link" href="#">3</a></li>
<li class="page-item">
<a class="page-link" href="#">Next</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</main>
{% endblock %}
{% block extra_js %}
{% endblock %}

View File

@@ -0,0 +1,124 @@
{% extends 'schasite/authenticated_base.html' %}
{% load static %}
{% block pagetitle %}
<title>Create Post | SCHA</title>
{% endblock %}
{% block content %}
<main class="py-5">
<div class="container">
<div class="row mb-4">
<div class="col-12">
<h1 class="display-6">Create New Post</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'dashboard' %}">Home</a></li>
<li class="breadcrumb-item"><a href="{% url 'posts' %}">Community Posts</a></li>
<li class="breadcrumb-item active" aria-current="page">Create Post</li>
</ol>
</nav>
</div>
</div>
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="card shadow-sm">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-pencil me-2"></i>New Community Post</h3>
</div>
<div class="card-body">
<form id="createPostForm" method="POST" action='{% url "posts_create" %}' class="needs-validation" novalidate>
{% csrf_token %}
{% if community_post.errors %}
<div class="alert alert-danger">
<i class="bi bi-info-circle-fill me-2"></i>
<p> Need to fix the following errors</p>
{{ community_post.errors | safe }}
</div>
{% endif %}
<div class="mb-3">
<label for="title" class="form-label">Title*</label>
<input type="text" class="form-control" value='{{ community_post.title.value|default_if_none:"" }}' name="title" id="title" required>
<div class="invalid-feedback">
Please provide a title for your post.
</div>
</div>
<div class="mb-3">
<label for="category" class="form-label">Category*</label>
<select class="form-select" name="category" id="category" required>
<option value='{{ community_post.category.value|default_if_none:"" }}' selected disabled>Select a category</option>
<option>Announcement</option>
<option>Discussion</option>
<option>For Sale</option>
<option>Help Needed</option>
<option>Event</option>
<option>Other</option>
</select>
<div class="invalid-feedback">
Please select a category.
</div>
</div>
<div class="mb-3">
<label for="content" class="form-label">Content*</label>
<textarea class="form-control" value='{{ community_post.content.value|default_if_none:"" }}' name="content" id="content" rows="6" required></textarea>
<div class="invalid-feedback">
Please provide content for your post.
</div>
</div>
<div class="mb-3">
<label for="postImage" class="form-label">Image (optional)</label>
<input class="form-control" type="file" id="postImage" accept="image/*">
<div class="form-text">
Maximum file size: 5MB. Allowed formats: JPG, PNG, GIF.
</div>
</div>
<div class="mb-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="notifyMembers">
<label class="form-check-label" for="notifyMembers">
Notify all members via email about this post
</label>
</div>
</div>
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
<a href="posts_create" class="btn btn-outline-secondary me-md-2">Cancel</a>
<button type="submit" class="btn btn-success">Post to Community</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</main>
{% endblock %}
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Form validation
const form = document.getElementById('createPostForm');
form.addEventListener('submit', function(event) {
event.preventDefault();
event.stopPropagation();
if (form.checkValidity()) {
// In a real implementation, this would submit the post
alert('Post created successfully (simulated)');
window.location.href = "../index.html";
}
form.classList.add('was-validated');
}, false);
// Image preview functionality would go here
});
</script>
{% endblock %}

View File

@@ -0,0 +1,328 @@
{% extends 'schasite/authenticated_base.html' %}
{% load static %}
{% block pagetitle %}
<title>Posts | SCHA</title>
<script type="text/css">
.post-detail .card-header {
padding: 1rem 1.25rem;
}
.comment-list {
max-height: 500px;
overflow-y: auto;
padding-right: 0.5rem;
}
.comment-list::-webkit-scrollbar {
width: 8px;
}
.comment-list::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
.comment-list::-webkit-scrollbar-thumb {
background: #c1c1c1;
border-radius: 10px;
}
.comment-list::-webkit-scrollbar-thumb:hover {
background: #a8a8a8;
}
/* Responsive adjustments */
@media (max-width: 767.98px) {
.comment-list {
max-height: none;
}
.post-detail .card-header h3 {
font-size: 1.1rem;
}
}
/* Interaction buttons */
.btn-outline-success:hover .bi-hand-thumbs-up,
.text-success .bi-hand-thumbs-up {
fill: currentColor;
}
</script>
{% endblock %}
{% block content %}
<main class="py-5">
<div class="container">
<div class="row mb-4">
<div class="col-12">
<h1 class="display-6">Community Post</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="../../index.html">Home</a></li>
<li class="breadcrumb-item"><a href="../index.html">Community Posts</a></li>
<li class="breadcrumb-item active" aria-current="page">Post Details</li>
</ol>
</nav>
</div>
</div>
<div class="row justify-content-center">
<div class="col-lg-8">
<!-- Main Post Card -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-success text-white">
<div class="d-flex justify-content-between align-items-center">
<h3 class="h5 mb-0">Annual Community Picnic</h3>
<span class="badge bg-light text-success">Announcement</span>
</div>
</div>
<div class="card-body">
<div class="d-flex mb-4">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
<i class="bi bi-person-fill text-success fs-4"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<h5 class="mb-1">Sarah Johnson</h5>
<div class="d-flex flex-wrap text-muted small">
<div class="me-3"><i class="bi bi-calendar me-1"></i>Posted on June 5, 2023</div>
<div><i class="bi bi-chat me-1"></i>12 comments</div>
</div>
</div>
</div>
<div class="mb-4">
<img src="../../../images/events/picnic.jpg" alt="Community picnic" class="img-fluid rounded mb-3">
<p>Our annual community picnic is scheduled for <strong>Saturday, June 15th from 11am-3pm</strong> at the Greenwood Park. This is always one of our most popular events, bringing together neighbors of all ages for a day of fun and community building.</p>
<h5 class="mt-4">Event Details</h5>
<ul>
<li><strong>Date:</strong> June 15, 2024</li>
<li><strong>Time:</strong> 11:00 AM - 3:00 PM</li>
<li><strong>Location:</strong> Greenwood Community Park</li>
<li><strong>Address:</strong> 1234 Greenwood Lane, Anytown, ST</li>
<li><strong>RSVP Deadline:</strong> June 10, 2024</li>
</ul>
<h5 class="mt-4">What to Expect</h5>
<p>This year we're excited to offer:</p>
<ul>
<li>Bounce house and playground activities for kids</li>
<li>Face painting and balloon animals</li>
<li>Pie baking contest (sign up below!)</li>
<li>Potluck lunch (main dishes provided by HOA)</li>
<li>Live acoustic music from 1-3pm</li>
</ul>
<div class="alert alert-success mt-4">
<h5><i class="bi bi-info-circle me-2"></i>Volunteers Needed</h5>
<p>We're looking for volunteers to help with setup (10-11am), food service (11:30am-1:30pm), and cleanup (3-4pm). Please email me if you can help!</p>
</div>
</div>
<div class="d-flex justify-content-between align-items-center">
<div>
<button class="btn btn-sm btn-outline-success me-2">
<i class="bi bi-hand-thumbs-up me-1"></i>Like (24)
</button>
<button class="btn btn-sm btn-outline-success">
<i class="bi bi-share me-1"></i>Share
</button>
</div>
<div>
<a href="#" class="text-decoration-none text-success small">
<i class="bi bi-flag me-1"></i>Report
</a>
</div>
</div>
</div>
</div>
<!-- Comments Section -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-chat-square-text me-2"></i>Comments (12)</h3>
</div>
<div class="card-body">
<!-- Comment Form -->
<div class="mb-4">
<form>
<div class="mb-3">
<label for="commentText" class="form-label">Add a comment</label>
<textarea class="form-control" id="commentText" rows="3" placeholder="Share your thoughts..."></textarea>
</div>
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-success">Post Comment</button>
</div>
</form>
</div>
<hr>
<!-- Comment List -->
<div class="comment-list">
<!-- Comment 1 -->
<div class="d-flex mb-4">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<div class="d-flex justify-content-between align-items-center mb-1">
<h6 class="mb-0">Robert Wilson</h6>
<small class="text-muted">June 5, 2023 at 2:30 PM</small>
</div>
<p class="mb-2">Looking forward to it! I'll bring my famous potato salad. Can I sign up to help with setup?</p>
<div>
<a href="#" class="text-decoration-none text-success small me-3">
<i class="bi bi-hand-thumbs-up me-1"></i>Like (5)
</a>
<a href="#" class="text-decoration-none text-success small">
<i class="bi bi-reply me-1"></i>Reply
</a>
</div>
<!-- Reply -->
<div class="d-flex mt-3">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 32px; height: 32px;">
<i class="bi bi-person-fill text-success small"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<div class="d-flex justify-content-between align-items-center mb-1">
<h6 class="mb-0">Sarah Johnson</h6>
<small class="text-muted">June 5, 2023 at 3:15 PM</small>
</div>
<p class="mb-2">That would be great Robert! I'll put you down for setup crew.</p>
<div>
<a href="#" class="text-decoration-none text-success small me-3">
<i class="bi bi-hand-thumbs-up me-1"></i>Like (2)
</a>
</div>
</div>
</div>
</div>
</div>
<!-- Comment 2 -->
<div class="d-flex mb-4">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<div class="d-flex justify-content-between align-items-center mb-1">
<h6 class="mb-0">Emily Davis</h6>
<small class="text-muted">June 6, 2023 at 9:45 AM</small>
</div>
<p class="mb-2">I'd love to enter the pie contest! Are there any category restrictions?</p>
<div>
<a href="#" class="text-decoration-none text-success small me-3">
<i class="bi bi-hand-thumbs-up me-1"></i>Like (3)
</a>
<a href="#" class="text-decoration-none text-success small">
<i class="bi bi-reply me-1"></i>Reply
</a>
</div>
</div>
</div>
<!-- Comment 3 -->
<div class="d-flex">
<div class="flex-shrink-0">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
<i class="bi bi-person-fill text-success"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<div class="d-flex justify-content-between align-items-center mb-1">
<h6 class="mb-0">Michael Brown</h6>
<small class="text-muted">June 6, 2023 at 11:20 AM</small>
</div>
<p class="mb-2">I'll bring my guitar and play during the music time if that's okay!</p>
<div>
<a href="#" class="text-decoration-none text-success small me-3">
<i class="bi bi-hand-thumbs-up me-1"></i>Like (8)
</a>
<a href="#" class="text-decoration-none text-success small">
<i class="bi bi-reply me-1"></i>Reply
</a>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer text-center">
<a href="#" class="text-decoration-none">Load more comments</a>
</div>
</div>
<!-- Related Posts -->
<div class="card shadow-sm">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-collection me-2"></i>Related Posts</h3>
</div>
<div class="card-body">
<div class="list-group list-group-flush">
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Summer Pool Schedule</h6>
<small class="text-muted">May 28, 2023</small>
</div>
<p class="mb-1 small">Pool hours and rules for the summer season</p>
<small class="text-muted">Posted by Michael Brown</small>
</a>
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Neighborhood Yard Sale</h6>
<small class="text-muted">May 15, 2023</small>
</div>
<p class="mb-1 small">Community-wide yard sale August 3-4</p>
<small class="text-muted">Posted by Emily Davis</small>
</a>
<a href="#" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Landscaping Updates</h6>
<small class="text-muted">April 30, 2023</small>
</div>
<p class="mb-1 small">New plants and improvements in common areas</p>
<small class="text-muted">Posted by Robert Wilson</small>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
{% endblock %}
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Like button functionality
document.querySelectorAll('.bi-hand-thumbs-up').forEach(icon => {
icon.addEventListener('click', function() {
// In a real implementation, this would track likes
this.classList.toggle('text-success');
});
});
// Comment submission
const commentForm = document.querySelector('.comment-list').previousElementSibling.querySelector('form');
commentForm.addEventListener('submit', function(e) {
e.preventDefault();
const commentText = document.getElementById('commentText').value;
if (commentText.trim() !== '') {
// In a real implementation, this would submit the comment
alert('Comment posted successfully (simulated)');
document.getElementById('commentText').value = '';
}
});
});
</script>
{% endblock %}

View File

@@ -74,7 +74,8 @@
<h2 class="h4 mb-0">New Member Information</h2> <h2 class="h4 mb-0">New Member Information</h2>
</div> </div>
<div class="card-body"> <div class="card-body">
<form id="membershipForm" class="needs-validation" novalidate> <form id="membershipForm" method="POST" action='{% url "membership_form2" %}' class="needs-validation" novalidate>
{% csrf_token %}
<!-- Household Information --> <!-- Household Information -->
<fieldset class="mb-4"> <fieldset class="mb-4">
<legend class="h5 text-success border-bottom pb-2">Household Information</legend> <legend class="h5 text-success border-bottom pb-2">Household Information</legend>
@@ -83,30 +84,27 @@
<div class="row g-3 mb-4"> <div class="row g-3 mb-4">
<div class="col-md-6"> <div class="col-md-6">
<label for="streetAddress" class="form-label">Street Address*</label> <label for="streetAddress" class="form-label">Street Address*</label>
<input type="text" class="form-control" id="streetAddress" required> <input type="text" class="form-control" id="streetAddress" name="streetAddress" required>
<div class="invalid-feedback"> <div class="invalid-feedback">
Please provide your street address. Please provide your street address.
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="unit" class="form-label">Unit/Apt #</label> <label for="unit" class="form-label">Unit/Apt #</label>
<input type="text" class="form-control" id="unit"> <input type="text" class="form-control" id="unit" name="unit">
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="city" class="form-label">City*</label> <label for="city" class="form-label">City*</label>
<input type="text" class="form-control" id="city" required> <input type="text" class="form-control" id="city" name="city" value="Wheaton" required>
<div class="invalid-feedback"> <div class="invalid-feedback">
Please provide your city. Please provide your city.
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="state" class="form-label">State*</label> <label for="state" class="form-label">State*</label>
<select class="form-select" id="state" required> <select class="form-select" id="state" name="state" value="Illinois" required>
<option value="" selected disabled>Choose...</option> <option value="" selected disabled>Choose...</option>
<option>Alabama</option> <option>IL</option>
<option>Alaska</option>
<!-- Add all states -->
<option>Wyoming</option>
</select> </select>
<div class="invalid-feedback"> <div class="invalid-feedback">
Please select your state. Please select your state.
@@ -114,7 +112,7 @@
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<label for="zipCode" class="form-label">ZIP Code*</label> <label for="zipCode" class="form-label">ZIP Code*</label>
<input type="text" class="form-control" id="zipCode" required> <input type="text" class="form-control" id="zipCode" value="zipCode" value="60189" required>
<div class="invalid-feedback"> <div class="invalid-feedback">
Please provide your ZIP code. Please provide your ZIP code.
</div> </div>
@@ -128,28 +126,28 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="firstName1" class="form-label">First Name*</label> <label for="firstName1" class="form-label">First Name*</label>
<input type="text" class="form-control" id="firstName1" required> <input type="text" class="form-control" id="firstName1" name="firstName1" required>
<div class="invalid-feedback"> <div class="invalid-feedback">
Please provide first name. Please provide first name.
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="lastName1" class="form-label">Last Name*</label> <label for="lastName1" class="form-label">Last Name*</label>
<input type="text" class="form-control" id="lastName1" required> <input type="text" class="form-control" id="lastName1" name="lastName1" required>
<div class="invalid-feedback"> <div class="invalid-feedback">
Please provide last name. Please provide last name.
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="email1" class="form-label">Email*</label> <label for="email1" class="form-label">Email*</label>
<input type="email" class="form-control" id="email1" required> <input type="email" class="form-control" id="email1" name="email1" required>
<div class="invalid-feedback"> <div class="invalid-feedback">
Please provide a valid email. Please provide a valid email.
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="phone1" class="form-label">Phone*</label> <label for="phone1" class="form-label">Phone*</label>
<input type="tel" class="form-control" id="phone1" required> <input type="tel" class="form-control" id="phone1" name="phone1" required>
<div class="invalid-feedback"> <div class="invalid-feedback">
Please provide phone number. Please provide phone number.
</div> </div>
@@ -174,19 +172,19 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="firstName2" class="form-label">First Name</label> <label for="firstName2" class="form-label">First Name</label>
<input type="text" class="form-control" id="firstName2"> <input type="text" class="form-control" id="firstName2" name="firstName2">
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="lastName2" class="form-label">Last Name</label> <label for="lastName2" class="form-label">Last Name</label>
<input type="text" class="form-control" id="lastName2"> <input type="text" class="form-control" id="lastName2" name="lastName2">
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="email2" class="form-label">Email</label> <label for="email2" class="form-label">Email</label>
<input type="email" class="form-control" id="email2"> <input type="email" class="form-control" id="email2" name="email2">
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<label for="phone2" class="form-label">Phone</label> <label for="phone2" class="form-label">Phone</label>
<input type="tel" class="form-control" id="phone2"> <input type="tel" class="form-control" id="phone2" name="phone2">
</div> </div>
</div> </div>
</fieldset> </fieldset>
@@ -199,50 +197,82 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="socialCommittee"> <input class="form-check-input" type="checkbox" id="block_captain" for="block_captain" name="block_captain">
<label class="form-check-label" for="socialCommittee"> <label class="form-check-label" id="block_captain">
Social & Events Committee BlockCaptain
</label> </label>
</div> </div>
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="landscapeCommittee"> <input class="form-check-input" type="checkbox" id="coordinator" for="coordinator" name="coordinator">
<label class="form-check-label" for="landscapeCommittee"> <label class="form-check-label" id="coordinator">
Landscape & Maintenance Committee Coordinator
</label> </label>
</div> </div>
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="safetyCommittee"> <input class="form-check-input" type="checkbox" id="egg_hunt" for="egg_hunt" name="egg_hunt">
<label class="form-check-label" for="safetyCommittee"> <label class="form-check-label" id="egg_hunt">
Safety & Neighborhood Watch Easter Egg Hunt
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="spring_garage_sale" for="spring_garage_sale" name="spring_garage_sale">
<label class="form-check-label" id="spring_garage_sale">
Spring Garage Sale
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="golf_outing" for="golf_outing" name="golf_outing">
<label class="form-check-label" id="golf_outing">
Golf Outing
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="ice_cream_social" for="ice_cream_social" name="ice_cream_social">
<label class="form-check-label" id="ice_cream_social">
Ice Creame Social
</label> </label>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="architecturalCommittee"> <input class="form-check-input" type="checkbox" id="fall_garage_sale" for="fall_garage_sale" name="fall_garage_sale">
<label class="form-check-label" for="architecturalCommittee"> <label class="form-check-label" id="fall_garage_sale">
Architectural Review Committee Fall Garage Sale
</label> </label>
</div> </div>
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="communicationsCommittee"> <input class="form-check-input" type="checkbox" id="halloween_party" for="halloween_party" name="halloween_party">
<label class="form-check-label" for="communicationsCommittee"> <label class="form-check-label" id="halloween_party">
Communications Committee Halloween Party
</label> </label>
</div> </div>
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="welcomingCommittee"> <input class="form-check-input" type="checkbox" id="santa_visit" for="santa_visit" name="santa_visit">
<label class="form-check-label" for="welcomingCommittee"> <label class="form-check-label" id="santa_visit">
Welcoming Committee Santa Visits
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="civic_affair" for="civic_affair" name="civic_affair">
<label class="form-check-label" id="civic_affair">
Civic Affairs Journalist
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="phone_directory" for="phone_directory" name="phone_directory">
<label class="form-check-label" id="phone_directory">
Annual Phone Director
</label>
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="no_preference" for="no_preference" name="no_preference">
<label class="form-check-label" id="no_preference">
No Preference
</label> </label>
</div> </div>
</div> </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> </fieldset>
<!-- Services Section --> <!-- Services Section -->
@@ -253,44 +283,54 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="babysitting"> <input class="form-check-input" type="checkbox" id="babysitting" for="babysitting" name="babysitting">
<label class="form-check-label" for="babysitting"> <label class="form-check-label" for="babysitting">
Babysitting Babysitting
</label> </label>
</div> </div>
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="lawnMowing"> <input class="form-check-input" type="checkbox" id="lawn_mowing" for="lawn_mowing" name="lawn_mowing">
<label class="form-check-label" for="lawnMowing"> <label class="form-check-label" for="lawn_mowing">
Lawn Mowing Lawn Mowing
</label> </label>
</div> </div>
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="snowShoveling"> <input class="form-check-input" type="checkbox" id="snow_shoveling" for="snow_shoveling" name="snow_shoveling">
<label class="form-check-label" for="snowShoveling"> <label class="form-check-label" for="snow_shoveling">
Snow Shoveling Snow Shoveling
</label> </label>
</div> </div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="leaf_raking" for="leaf_raking" name="leaf_raking">
<label class="form-check-label" for="leaf_raking">
Lead Raking
</label>
</div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="houseSitting"> <input class="form-check-input" type="checkbox" id="house_sitting" for="house_sitting" name="house_sitting">
<label class="form-check-label" for="houseSitting"> <label class="form-check-label" for="house_sitting">
House Sitting House Sitting
</label> </label>
</div> </div>
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="petCare"> <input class="form-check-input" type="checkbox" id="petsitting" for="petsitting" name="petsitting">
<label class="form-check-label" for="petCare"> <label class="form-check-label" for="petsitting">
Pet Care Pet Sitting
</label> </label>
</div> </div>
<div class="form-check mb-2"> <div class="form-check mb-2">
<input class="form-check-input" type="checkbox" id="otherService"> <input class="form-check-input" type="checkbox" id="other" for="other" name="other">
<label class="form-check-label" for="otherService"> <label class="form-check-label" for="other">
Other (please specify) Other (please specify)
</label> </label>
</div> </div>
</div> </div>
<div class="mt-3">
<label for="other_desc" class="form-label">Other interests or skills you'd like to contribute:</label>
<textarea class="form-control" id="other_desc" for="other_desc" name="other_desc" rows="2"></textarea>
</div>
</div> </div>
<div class="mt-3" id="otherServiceSpecify" style="display: none;"> <div class="mt-3" id="otherServiceSpecify" style="display: none;">
@@ -299,20 +339,10 @@
</div> </div>
</fieldset> </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"> <div class="d-grid gap-2 d-md-flex justify-content-md-end">
{% if captchaForm %}
{{ captchaForm }}
{% endif %}
<button class="btn btn-outline-secondary me-md-2" type="reset">Reset Form</button> <button class="btn btn-outline-secondary me-md-2" type="reset">Reset Form</button>
<button class="btn btn-success" type="submit">Submit Membership</button> <button class="btn btn-success" type="submit">Submit Membership</button>
</div> </div>
@@ -324,35 +354,6 @@
</div> </div>
</main> </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 %} {% endblock %}

View File

@@ -0,0 +1,47 @@
{% extends 'schasite/base2.html' %}
{% load static %}
{% block pagetitle %}
<title>Password Reset | SCHA</title>
{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card shadow-sm">
<div class="card-header bg-success text-white text-center">
<h2><i class="bi bi-key me-2"></i>Reset Password</h2>
</div>
<div class="card-body p-4">
<form id="resetForm" class="needs-validation" novalidate>
<div class="mb-4 text-center">
<p>Enter your email address and we'll send you a link to reset your password.</p>
</div>
<div class="mb-3">
<label for="resetEmail" class="form-label">Email Address</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-envelope"></i></span>
<input type="email" class="form-control" id="resetEmail" required>
<div class="invalid-feedback">
Please provide a valid email address.
</div>
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-success">
<i class="bi bi-send me-2"></i>Send Reset Link
</button>
</div>
</form>
</div>
<div class="card-footer text-center">
<a href="login.html" class="text-decoration-none"><i class="bi bi-arrow-left me-1"></i>Back to Login</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,396 @@
{% extends 'schasite/authenticated_base.html' %}
{% load static %}
{% block pagetitle %}
<title>Profile | SCHA</title>
<script type="text/css">
.profile-avatar {
width: 120px;
height: 120px;
margin: 0 auto 1rem;
}
.dues-history table {
font-size: 0.875rem;
}
.dues-history th,
.dues-history td {
padding: 0.5rem;
}
.list-group-item-action:hover {
background-color: rgba(25, 135, 84, 0.05);
}
/* Communication Preferences */
#preferencesForm .form-check {
margin-bottom: 0.5rem;
}
/* Responsive adjustments */
@media (max-width: 991.98px) {
.profile-avatar {
width: 80px;
height: 80px;
}
}
@media (max-width: 767.98px) {
.profile-avatar {
width: 100px;
height: 100px;
}
}
</script>
{% endblock %}
{% block content %}
<main class="py-5">
<div class="container">
<div class="row mb-4">
<div class="col-12">
<h1 class="display-6">My Profile</h1>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="../index.html">Home</a></li>
<li class="breadcrumb-item active" aria-current="page">Profile</li>
</ol>
</nav>
</div>
</div>
<div class="row g-4">
<!-- Left Column -->
<div class="col-lg-4">
<!-- Member Card -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-person-badge me-2"></i>Profile</h3>
</div>
<div class="card-body text-center">
<div class="mb-3">
<div class="bg-success bg-opacity-10 rounded-circle d-flex align-items-center justify-content-center mx-auto" style="width: 120px; height: 120px;">
<i class="bi bi-person-fill text-success fs-1"></i>
</div>
</div>
<h4 class="card-title">John Doe</h4>
<p class="text-muted">Member since: January 15, 2020</p>
<hr>
<button class="btn btn-outline-success btn-sm mb-3">
<i class="bi bi-pencil me-1"></i>Edit Profile
</button>
<button class="btn btn-outline-success btn-sm mb-3">
<i class="bi bi-lock me-1"></i>Change Password
</button>
</div>
</div>
<!-- Dues Payment History -->
<div class="card shadow-sm">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-credit-card me-2"></i>Dues Payment History</h3>
</div>
<div class="card-body">
<div class="alert alert-success">
<i class="bi bi-check-circle-fill me-2"></i>
<strong>Current Status:</strong> Paid through December 2024
</div>
<div class="table-responsive">
<table class="table table-sm">
<thead>
<tr>
<th>Date</th>
<th>Amount</th>
<th>Method</th>
<th>Receipt</th>
</tr>
</thead>
<tbody>
<tr>
<td>Jan 10, 2024</td>
<td>$600.00</td>
<td>Credit Card</td>
<td><a href="#" class="text-success">View</a></td>
</tr>
<tr>
<td>Jan 5, 2023</td>
<td>$550.00</td>
<td>Bank Transfer</td>
<td><a href="#" class="text-success">View</a></td>
</tr>
<tr>
<td>Jan 8, 2022</td>
<td>$525.00</td>
<td>Check</td>
<td><a href="#" class="text-success">View</a></td>
</tr>
<tr>
<td>Jan 12, 2021</td>
<td>$500.00</td>
<td>Credit Card</td>
<td><a href="#" class="text-success">View</a></td>
</tr>
<tr>
<td>Jan 15, 2020</td>
<td>$500.00</td>
<td>Check</td>
<td><a href="#" class="text-success">View</a></td>
</tr>
</tbody>
</table>
</div>
<div class="text-center mt-2">
<a href="#" class="btn btn-sm btn-success">
<i class="bi bi-printer me-1"></i>Print Full History
</a>
</div>
</div>
</div>
</div>
<!-- Right Column -->
<div class="col-lg-8">
<!-- Member Details -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-success text-white">
<div class="d-flex justify-content-between align-items-center">
<h3 class="h5 mb-0"><i class="bi bi-house-heart me-2"></i>Member Details</h3>
<button class="btn btn-sm btn-light" id="editDetailsBtn">
<i class="bi bi-pencil me-1"></i>Edit
</button>
</div>
</div>
<div class="card-body">
<form id="memberDetailsForm">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">First Name</label>
<input type="text" class="form-control" value="John" disabled>
</div>
<div class="col-md-6">
<label class="form-label">Last Name</label>
<input type="text" class="form-control" value="Doe" disabled>
</div>
<div class="col-md-6">
<label class="form-label">Email</label>
<input type="email" class="form-control" value="john.doe@example.com" disabled>
</div>
<div class="col-md-6">
<label class="form-label">Phone</label>
<input type="tel" class="form-control" value="(555) 123-4567" disabled>
</div>
<div class="col-12">
<label class="form-label">Street Address</label>
<input type="text" class="form-control" value="123 Oak Drive" disabled>
</div>
<div class="col-md-4">
<label class="form-label">City</label>
<input type="text" class="form-control" value="Anytown" disabled>
</div>
<div class="col-md-4">
<label class="form-label">State</label>
<input type="text" class="form-control" value="ST" disabled>
</div>
<div class="col-md-4">
<label class="form-label">ZIP Code</label>
<input type="text" class="form-control" value="12345" disabled>
</div>
<div class="col-12 mt-3" id="saveDetailsBtn" style="display: none;">
<button type="submit" class="btn btn-success me-2">Save Changes</button>
<button type="button" class="btn btn-outline-secondary" id="cancelEditBtn">Cancel</button>
</div>
</div>
</form>
</div>
</div>
<!-- My Posts -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-pencil-square me-2"></i>My Posts</h3>
</div>
<div class="card-body">
<div class="list-group list-group-flush">
<a href="../posts/detail.html" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Looking for lawn service recommendations</h6>
<small class="text-muted">May 28, 2024</small>
</div>
<p class="mb-1 small">Does anyone have a landscaper they would recommend for weekly mowing?</p>
<small><span class="badge bg-success bg-opacity-10 text-success">Discussion</span> • 5 comments</small>
</a>
<a href="../posts/detail.html" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Lost cat - gray tabby</h6>
<small class="text-muted">April 15, 2024</small>
</div>
<p class="mb-1 small">Our cat Mittens got out last night near Oak Drive. Please contact if seen!</p>
<small><span class="badge bg-success bg-opacity-10 text-success">Help Needed</span> • 12 comments</small>
</a>
<a href="../posts/detail.html" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Garage sale this weekend</h6>
<small class="text-muted">March 30, 2024</small>
</div>
<p class="mb-1 small">We're having a garage sale Saturday 8am-2pm with furniture, tools, and more.</p>
<small><span class="badge bg-success bg-opacity-10 text-success">For Sale</span> • 3 comments</small>
</a>
</div>
<div class="text-center mt-3">
<a href="../posts/" class="btn btn-sm btn-outline-success">View All My Posts</a>
</div>
</div>
</div>
<!-- My Comments -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-chat-left-text me-2"></i>Posts I've Commented On</h3>
</div>
<div class="card-body">
<div class="list-group list-group-flush">
<a href="../posts/detail.html" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Annual Community Picnic</h6>
<small class="text-muted">June 5, 2024</small>
</div>
<p class="mb-1 small"><strong>My comment:</strong> "Looking forward to it! I'll bring potato salad."</p>
<small><span class="badge bg-success bg-opacity-10 text-success">Announcement</span> • 24 comments total</small>
</a>
<a href="../posts/detail.html" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Pool opening delayed</h6>
<small class="text-muted">May 20, 2024</small>
</div>
<p class="mb-1 small"><strong>My comment:</strong> "Thanks for the update. Any estimate on opening date?"</p>
<small><span class="badge bg-success bg-opacity-10 text-success">Announcement</span> • 8 comments total</small>
</a>
<a href="../posts/detail.html" class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1">Neighborhood watch meeting</h6>
<small class="text-muted">April 10, 2024</small>
</div>
<p class="mb-1 small"><strong>My comment:</strong> "I can help organize the next meeting."</p>
<small><span class="badge bg-success bg-opacity-10 text-success">Event</span> • 15 comments total</small>
</a>
</div>
<div class="text-center mt-3">
<a href="../posts/" class="btn btn-sm btn-outline-success">View All My Comments</a>
</div>
</div>
</div>
<!-- Communication Preferences -->
<div class="card shadow-sm">
<div class="card-header bg-success text-white">
<h3 class="h5 mb-0"><i class="bi bi-envelope me-2"></i>Communication Preferences</h3>
</div>
<div class="card-body">
<form id="preferencesForm">
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="emailNotifications" checked>
<label class="form-check-label" for="emailNotifications">
Receive email notifications
</label>
</div>
</div>
<div class="mb-3">
<h6 class="mb-2">Notification Types:</h6>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="postReplies" checked>
<label class="form-check-label" for="postReplies">
When someone replies to my posts
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="commentReplies" checked>
<label class="form-check-label" for="commentReplies">
When someone replies to my comments
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="communityAnnouncements" checked>
<label class="form-check-label" for="communityAnnouncements">
Community announcements
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="eventReminders">
<label class="form-check-label" for="eventReminders">
Event reminders
</label>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="showInDirectory" checked>
<label class="form-check-label" for="showInDirectory">
Include my contact information in the member directory
</label>
</div>
<div class="form-text">
Uncheck to hide your email and phone number from other members
</div>
</div>
<div class="mt-4">
<button type="submit" class="btn btn-success">Save Preferences</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</main>
{% endblock %}
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Edit details toggle
const editBtn = document.getElementById('editDetailsBtn');
const cancelBtn = document.getElementById('cancelEditBtn');
const saveBtn = document.getElementById('saveDetailsBtn');
const formInputs = document.querySelectorAll('#memberDetailsForm input');
editBtn.addEventListener('click', function() {
formInputs.forEach(input => {
input.disabled = false;
});
saveBtn.style.display = 'block';
editBtn.style.display = 'none';
});
cancelBtn.addEventListener('click', function() {
formInputs.forEach(input => {
input.disabled = true;
});
saveBtn.style.display = 'none';
editBtn.style.display = 'block';
});
// Form submissions
document.getElementById('memberDetailsForm').addEventListener('submit', function(e) {
e.preventDefault();
// In a real implementation, this would save the changes
alert('Profile details updated successfully (simulated)');
formInputs.forEach(input => {
input.disabled = true;
});
saveBtn.style.display = 'none';
editBtn.style.display = 'block';
});
document.getElementById('preferencesForm').addEventListener('submit', function(e) {
e.preventDefault();
// In a real implementation, this would save preferences
alert('Communication preferences saved (simulated)');
});
});
</script>
{% endblock %}

View File

@@ -45,7 +45,6 @@
<div class="card-body text-center"> <div class="card-body text-center">
<h3 class="text-success mb-3">2025-2026 Stonehedge Community Homeowners Association Board</h3> <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>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>
</div> </div>
@@ -63,7 +62,7 @@
<h4 class="mb-0">{{ officer.position }}</h4> <h4 class="mb-0">{{ officer.position }}</h4>
</div> </div>
<div class="card-body text-center"> <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"> <!--<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> <h5 class="card-title">{{ officer.name }}</h5>
<!-- <p class="text-muted">Term: 2022-2024</p> <!-- <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> --> <p class="card-text">John has lived in Greenwood Estates since 2015 and brings 20 years of financial management experience to the board.</p> -->
@@ -85,7 +84,7 @@
<h4 class="mb-0">TBD</h4> <h4 class="mb-0">TBD</h4>
</div> </div>
<div class="card-body text-center"> <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"> <!--<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> <h5 class="card-title">TBD</h5>
<p class="text-muted">TBD</p> <p class="text-muted">TBD</p>
<p class="card-text">TBD</p> <p class="card-text">TBD</p>
@@ -97,53 +96,7 @@
{% endfor %} {% 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> </div>
</main> </main>
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,127 @@
{% extends 'schasite/base2.html' %}
{% load static %}
{% block pagetitle %}
<title>Set Password | SCHA</title>
{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card shadow-sm">
<div class="card-header bg-success text-white text-center">
<h2><i class="bi bi-key me-2"></i>Set New Password</h2>
</div>
<div class="card-body p-4">
<form id="passwordForm" class="needs-validation" novalidate>
<div class="mb-3">
<label for="newPassword" class="form-label">New Password</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-lock"></i></span>
<input type="password" class="form-control" id="newPassword" required
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}">
<button class="btn btn-outline-secondary" type="button" id="toggleNewPassword">
<i class="bi bi-eye"></i>
</button>
<div class="invalid-feedback">
Password must contain at least 8 characters with uppercase, lowercase, and a number.
</div>
</div>
<div class="form-text">
<small>Must be at least 8 characters with uppercase, lowercase, and a number.</small>
</div>
</div>
<div class="mb-4">
<label for="confirmPassword" class="form-label">Confirm Password</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-lock"></i></span>
<input type="password" class="form-control" id="confirmPassword" required>
<button class="btn btn-outline-secondary" type="button" id="toggleConfirmPassword">
<i class="bi bi-eye"></i>
</button>
<div class="invalid-feedback">
Passwords must match.
</div>
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-success">
<i class="bi bi-check-circle me-2"></i>Set Password
</button>
</div>
</form>
</div>
<div class="card-footer text-center">
<a href="login.html" class="text-decoration-none"><i class="bi bi-arrow-left me-1"></i>Back to Login</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
document.addEventListener('DOMContentLoaded', function() {
// Toggle password visibility
const toggleNewPassword = document.getElementById('toggleNewPassword');
const newPasswordInput = document.getElementById('newPassword');
const toggleConfirmPassword = document.getElementById('toggleConfirmPassword');
const confirmPasswordInput = document.getElementById('confirmPassword');
toggleNewPassword.addEventListener('click', function() {
const type = newPasswordInput.getAttribute('type') === 'password' ? 'text' : 'password';
newPasswordInput.setAttribute('type', type);
this.innerHTML = type === 'password' ? '<i class="bi bi-eye"></i>' : '<i class="bi bi-eye-slash"></i>';
});
toggleConfirmPassword.addEventListener('click', function() {
const type = confirmPasswordInput.getAttribute('type') === 'password' ? 'text' : 'password';
confirmPasswordInput.setAttribute('type', type);
this.innerHTML = type === 'password' ? '<i class="bi bi-eye"></i>' : '<i class="bi bi-eye-slash"></i>';
});
// Form validation
const form = document.getElementById('passwordForm');
form.addEventListener('submit', function(event) {
event.preventDefault();
event.stopPropagation();
// Check if passwords match
if (newPasswordInput.value !== confirmPasswordInput.value) {
confirmPasswordInput.setCustomValidity("Passwords must match");
} else {
confirmPasswordInput.setCustomValidity("");
}
if (form.checkValidity()) {
// In a real implementation, this would update the password
alert('Password successfully updated (simulated)');
window.location.href = "login.html";
}
form.classList.add('was-validated');
}, false);
// Live password validation
newPasswordInput.addEventListener('input', function() {
if (this.value !== confirmPasswordInput.value) {
confirmPasswordInput.setCustomValidity("Passwords must match");
} else {
confirmPasswordInput.setCustomValidity("");
}
});
confirmPasswordInput.addEventListener('input', function() {
if (this.value !== newPasswordInput.value) {
this.setCustomValidity("Passwords must match");
} else {
this.setCustomValidity("");
}
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,66 @@
{% extends 'schasite/base2.html' %}
{% load static %}
{% block pagetitle %}
<title>Login | SCHA</title>
{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card shadow-sm">
<div class="card-header bg-success text-white text-center">
<h2><i class="bi bi-shield-lock me-2"></i>Member Login</h2>
</div>
<div class="card-body p-4">
<form id="loginForm" class="needs-validation" novalidate>
<div class="mb-3">
<label for="loginEmail" class="form-label">Email Address</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-envelope"></i></span>
<input type="email" class="form-control" id="loginEmail" required>
<div class="invalid-feedback">
Please provide a valid email address.
</div>
</div>
</div>
<div class="mb-3">
<label for="loginPassword" class="form-label">Password</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-key"></i></span>
<input type="password" class="form-control" id="loginPassword" required>
<button class="btn btn-outline-secondary" type="button" id="togglePassword">
<i class="bi bi-eye"></i>
</button>
<div class="invalid-feedback">
Please enter your password.
</div>
</div>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="rememberMe">
<label class="form-check-label" for="rememberMe">Remember me</label>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-success">
<i class="bi bi-box-arrow-in-right me-2"></i>Sign In
</button>
</div>
<div class="mt-3 text-center">
<a href="{% url 'password_reset' %}" class="text-decoration-none">Forgot password?</a>
</div>
</form>
</div>
<div class="card-footer text-center text-muted">
Not a member? <a href="{% url 'signup' %}" class="text-success">Join our community</a>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,104 @@
{% extends 'schasite/base2.html' %}
{% load static %}
{% block pagetitle %}
<title>Login | SCHA</title>
{% endblock %}
{% block content %}
<div class="container py-5">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6">
<div class="card shadow-sm">
<div class="card-header bg-success text-white text-center">
<h2><i class="bi bi-shield-lock me-2"></i>Member Signup</h2>
</div>
<div class="card-body p-4">
<form id="loginForm" class="needs-validation" novalidate>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label class="form-label">First Name</label>
<input type="text" class="form-control" value="John">
</div>
<div class="col-md-6">
<label class="form-label">Last Name</label>
<input type="text" class="form-control" value="Doe">
</div>
<div class="col-md-6">
<label class="form-label">Email</label>
<input type="email" class="form-control" value="john.doe@example.com">
</div>
<div class="col-md-6">
<label class="form-label">Phone</label>
<input type="tel" class="form-control" value="(555) 123-4567">
</div>
<div class="col-12">
<label class="form-label">Street Address</label>
<input type="text" class="form-control" value="123 Oak Drive">
</div>
<div class="col-md-4">
<label class="form-label">City</label>
<input type="text" class="form-control" value="Anytown">
</div>
<div class="col-md-4">
<label class="form-label">State</label>
<input type="text" class="form-control" value="ST">
</div>
<div class="col-md-4">
<label class="form-label">ZIP Code</label>
<input type="text" class="form-control" value="12345" >
</div>
<div class="col-md-12">
<label for="loginPassword" class="form-label">Password</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-key"></i></span>
<input type="password" class="form-control" id="loginPassword" required>
<button class="btn btn-outline-secondary" type="button" id="togglePassword">
<i class="bi bi-eye"></i>
</button>
<div class="invalid-feedback">
Please enter your password.
</div>
</div>
</div>
<div class="col-md-12">
<label for="loginPassword" class="form-label">Confirm Password</label>
<div class="input-group">
<span class="input-group-text"><i class="bi bi-key"></i></span>
<input type="password" class="form-control" id="loginPassword" required>
<button class="btn btn-outline-secondary" type="button" id="togglePassword">
<i class="bi bi-eye"></i>
</button>
<div class="invalid-feedback">
Please confirm your password. </div>
</div>
</div>
</div>
</div>
<div class="d-grid gap-2">
<button type="submit" class="btn btn-success">
<i class="bi bi-box-arrow-in-right me-2"></i>Sign Up
</button>
</div>
<div class="mt-3 text-center">
<a href="{% url 'login' %}" class="text-decoration-none">Back to Log In</a>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -51,7 +51,7 @@
<div class="col-lg-10"> <div class="col-lg-10">
<div class="card shadow-sm"> <div class="card shadow-sm">
<div class="card-header bg-success text-white"> <div class="card-header bg-success text-white">
<h3 class="mb-0"><i class="bi bi-exclamation-triangle me-2"></i>Useful Links</h3> <h3 class="mb-0">Useful Links</h3>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="table-responsive"> <div class="table-responsive">

View File

@@ -1,26 +1,50 @@
from django.urls import path from django.urls import path
from django.conf import settings
from . import views from . import views
urlpatterns = [
path("", views.index, name="index"), if settings.DEBUG:
path("useful_links", views.useful_links, name="useful_links"), authenticated_views = [
path("about_us", views.about_us, name="about_us"), path('login/',views.login, name="login"),
path("calendar", views.calendar, name="calendar"), path('set_password/',views.set_password, name="set_password"),
path("newsletters", views.newsletters, name="newsletters"), path('password_reset/',views.password_reset, name="password_reset"),
path("dues", views.dues, name="dues"), path('signup/',views.signup, name="signup"),
path("membership_form", views.membership_form, name="membership_form"), path('directory/',views.member_directory, name="directory"),
path("scha_board", views.scha_board, name="scha_board"), path('dashboard/',views.member_dashboard, name="dashboard"),
path('posts/',views.member_posts, name="posts"),
path('posts_create/',views.member_posts_create, name="posts_create"),
path('posts/<int:post_id>',views.member_posts_detail, name="posts_detail"),
path('profile/',views.profile, name="profile"),
]
else:
authenticated_views = []
urlpatterns = authenticated_views + [
# path("", views.index, name="index"),
# path("useful_links", views.useful_links, name="useful_links"),
# path("about_us", views.about_us, name="about_us"),
# path("calendar", views.calendar, name="calendar"),
# path("newsletters", views.newsletters, name="newsletters"),
# 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 # updated UI urls
path("index2", views.index2, name="index2"), path("", views.index2, name="index2"),
path("about_us2", views.about_us2, name="about_us2"), path("about_us", views.about_us2, name="about_us2"),
path("calendar2", views.calendar2, name="calendar2"), path("calendar", views.calendar2, name="calendar2"),
path("newsletters2", views.newsletters2, name="newsletters2"), path("newsletters", views.newsletters2, name="newsletters2"),
path("dues2", views.dues2, name="dues2"), path("dues", views.dues2, name="dues2"),
path("membership_form2", views.membership_form2, name="membership_form2"), path("membership_form", views.membership_form2, name="membership_form2"),
path("scha_board2", views.scha_board2, name="scha_board2"), path("scha_board", views.scha_board2, name="scha_board2"),
path("useful_links2", views.useful_links2, name="useful_links2"), path("useful_links", views.useful_links2, name="useful_links2"),
# stripe specific urls below # stripe specific urls below
path("config/", views.stripe_config), path("config/", views.stripe_config),
path("create-checkout-session/", views.create_checkout_session), path("create-checkout-session/", views.create_checkout_session),
# authenticated views
] ]

View File

@@ -1,20 +1,29 @@
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from .models import UsefulLinks, CalendarEvent, MembershipPerson, Payments, SCHAOfficer
from .models import UsefulLinks, CalendarEvent, MembershipPerson, Payments, SCHAOfficer, Membership, CommunityPost
from .forms import ( from .forms import (
ChildrenForm, ChildrenForm,
CommunityPostForm,
AddressForm, AddressForm,
PeopleForm, PeopleForm,
CommitteeForm, CommitteeForm,
ServicesForm, ServicesForm,
) # , CaptchaForm CaptchaForm
)
from django.db import transaction, IntegrityError from django.db import transaction, IntegrityError
# Stripe required imports # Stripe required imports
from django.conf import settings # new from django.conf import settings # new
from django.http.response import JsonResponse, HttpResponse from django.http.response import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt # new from django.views.decorators.csrf import csrf_exempt # new
from django.contrib.auth import logout, authenticate, login
from django.contrib.auth.decorators import login_required
import stripe import stripe
import logging
logger = logging.getLogger(__name__)
print(__name__)
""" """
Strip Stuff Strip Stuff
Tutorial: https://testdriven.io/blog/django-stripe-tutorial/ Tutorial: https://testdriven.io/blog/django-stripe-tutorial/
@@ -34,28 +43,27 @@ def stripe_config(request):
@csrf_exempt @csrf_exempt
def create_checkout_session(request): def create_checkout_session(request):
if request.method == "GET": if request.method == 'GET':
domain_url = "http://localhost:8000/" domain_url ="https://www.schawheaton.com/"
stripe.api_key = settings.STRIPE_SECRET_KEY stripe.api_key = settings.STRIPE_SECRET_KEY
try: try:
checkout_session = stripe.checkout.Session.create( checkout_session = stripe.checkout.Session.create(
success_url=domain_url + "success?session_id={CHECKOUT_SESSION_ID}", success_url = domain_url+'success?session_id={CHECKOUT_SESSION_ID}',
cancel_url=domain_url + "cancelled/", cancel_url = domain_url+'cancelled/',
payment_method_types=["card"], payment_method_types=['card'],
mode="payment", mode="payment",
line_items=[ line_items = [{
{ # 'name':'SCHA Dues',
# 'name':'SCHA Dues', 'quantity': 1,
"quantity": 1, # 'currency': 'usd',
# 'currency': 'usd', 'price':'price_1P5KBtIbGKYTLTtMJ0Rh1jMu',
"price": "price_1OxZLfDV0RPXOyxG5ipjhUXk", #'price_1P5K7uIbGKYTLTtMFNxbkA8X'
} #'price_1OxZLfDV0RPXOyxG5ipjhUXk',
], }]
) )
return JsonResponse({"sessionId": checkout_session["id"]}) return JsonResponse({'sessionId': checkout_session['id']})
except Exception as e: except Exception as e:
return JsonResponse({"error": str(e)}) return JsonResponse({'error': str(e)})
def stripe_success(request): def stripe_success(request):
return render(request, "schasite/dues_success.html", {}) return render(request, "schasite/dues_success.html", {})
@@ -70,11 +78,13 @@ def stripe_webhook(request):
stripe.api_key = settings.STRIPE_SECRET_KEY stripe.api_key = settings.STRIPE_SECRET_KEY
endpoint_secret = settings.STRIPE_ENDPOINT_SECRET endpoint_secret = settings.STRIPE_ENDPOINT_SECRET
payload = request.body payload = request.body
sig_header = request.META["HTTP_STRIPE_SIGNATURE"] sig_header = request.META['HTTP_STRIPE_SIGNATURE']
event = None event = None
try: 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: except ValueError as e:
# Invalid payload # Invalid payload
return HttpResponse(status=400) return HttpResponse(status=400)
@@ -83,17 +93,18 @@ def stripe_webhook(request):
return HttpResponse(status=400) return HttpResponse(status=400)
# Handle the checkout.session.completed event # Handle the checkout.session.completed event
if event["type"] == "checkout.session.completed": if event['type'] == 'checkout.session.completed':
email = None email = None
try: try:
email = event["data"]["object"]["customer_details"]["email"] email = event['data']['object']['customer_details']['email']
except: except:
pass pass
person = MembershipPerson.objects.filter( person = MembershipPerson.objects.filter(email=email).first() # just take the first
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 # try to link to a member
payment.save() payment.save()
# TODO: run some custom code here # TODO: run some custom code here
@@ -141,24 +152,23 @@ def calendar2(request):
def scha_board2(request): def scha_board2(request):
def get_officer(position_name): def get_officers(position_name) -> list[SCHAOfficer]:
try: try:
return SCHAOfficer.objects.get(position=position_name) return SCHAOfficer.objects.filter(position=position_name)
except: except:
return None return None
officers = [ officers = []
get_officer("President"), for position in ["President","1st Vice President",
get_officer("1st Vice President"), "2nd Vice President",
get_officer("2nd Vice President"), "Treasurer",
get_officer("Treasurer"), "Secretary",
get_officer("Secretary"), "Website",
get_officer("Website"), "Membership",
get_officer("Membership"), "Directory",
get_officer("Directory"), "Facebook",
get_officer("Facebook"), "Eblasts"]:
get_officer("Eblasts"), officers += get_officers(position)
]
return render(request, "schasite/scha_board2.html", {"officers": officers}) return render(request, "schasite/scha_board2.html", {"officers": officers})
@@ -178,77 +188,130 @@ def membership_form2(request):
if request.method == "POST": if request.method == "POST":
# before we pass in the data we want to sanitize the phone numbers # before we pass in the data we want to sanitize the phone numbers
post_data = request.POST.copy() address_data = {
post_data.update( "address_1": request.POST.get("streetAddress", ""),
{ "address_2": request.POST.get("unit", ""),
"person1-phone_number": sanitize_phone_number( "city": request.POST.get("city", "60189"),
post_data["person1-phone_number"] "state": request.POST.get("state", "IL"),
) "zip_code": request.POST.get("zipCode", "60189"),
} }
) logging.debug(address_data)
post_data.update( addressForm = AddressForm(address_data)
{ logging.debug(f"is addressForm valid: {addressForm.is_valid()}")
"person2-phone_number": sanitize_phone_number( if not addressForm.is_valid():
post_data["person2-phone_number"] logging.error(f"addressForm Errors: {addressForm.errors}")
)
} services_data = {
) "babysitting": True if request.POST.get("babysitting", "") == "on" else False,
membershipForm = ChildrenForm(post_data) "lawn_mowing": True if request.POST.get("lawn_mowing", "") == "on" else False,
addressForm = AddressForm(post_data) "snow_shoveling": True if request.POST.get("snow_shoveling", "") == "on" else False,
peopleForm1 = PeopleForm(post_data, prefix="person1") "leaf_raking": True if request.POST.get("leaf_raking", "") == "on" else False,
peopleForm2 = PeopleForm(post_data, prefix="person2") "petsitting": True if request.POST.get("petsitting", "") == "on" else False,
servicesForm = ServicesForm(post_data) "house_sitting": True if request.POST.get("house_sitting", "") == "on" else False,
committeeForm = CommitteeForm(post_data) "other": True if request.POST.get("other", "") == "on" else False,
# captchaForm = CaptchaForm(post_data) "other_desc": True if request.POST.get("other_desc", "") == "on" else False,
}
committee_data = {
"block_captain": True if request.POST.get("block_captain", "") == "on" else False,
"coordinator": True if request.POST.get("coordinator", "") == "on" else False,
"egg_hunt": True if request.POST.get("egg_hunt", "") == "on" else False,
"spring_garage_sale": True if request.POST.get("spring_garage_sale", "") == "on" else False,
"golf_outing": True if request.POST.get("golf_outing", "") == "on" else False,
"ice_cream_social": True if request.POST.get("ice_cream_social", "") == "on" else False,
"fall_garage_sale": True if request.POST.get("fall_garage_sale", "") == "on" else False,
"halloween_party": True if request.POST.get("halloween_party", "") == "on" else False,
"santa_visit": True if request.POST.get("santa_visit", "") == "on" else False,
"website": True if request.POST.get("website", "") == "on" else False,
"civic_affair": True if request.POST.get("civic_affair", "") == "on" else False,
"phone_directory": True if request.POST.get("phone_directory", "") == "on" else False,
"no_preference": True if request.POST.get("no_preference", "") == "on" else False,
}
servicesForm = ServicesForm(services_data)
committeeForm = CommitteeForm(committee_data)
person1_data = {
"first_name": request.POST.get("firstName1"),
"last_name": request.POST.get("lastName1"),
"email": request.POST.get("email1"),
"phone_number": sanitize_phone_number(request.POST.get("phone1")),
}
person2_data = {
"frist_name": request.POST.get("firstName2"),
"last_name": request.POST.get("lastName2"),
"email": request.POST.get("email2"),
"phone_number": sanitize_phone_number(request.POST.get("phone2")),
}
peopleForm1 = PeopleForm(person1_data)
peopleForm2 = PeopleForm(person2_data)
logging.debug("Validating the captcha form")
logging.debug(request.POST.get("captcha",""))
captchaForm = CaptchaForm({
"captcha": request.POST.get("captcha","")
})
logging.debug(f"Captch form is: {captchaForm.is_valid()}")
logging.debug(f"peopleForm1 form is: {peopleForm1.is_valid()}")
logging.debug(f"peopleForm2 form is: {peopleForm2.is_valid()}")
logging.debug(f"servicesForm form is: {servicesForm.is_valid()}")
if ( if (
membershipForm.is_valid() addressForm.is_valid()
and addressForm.is_valid()
and committeeForm.is_valid() and committeeForm.is_valid()
and (peopleForm1.is_valid() or peopleForm2.is_valid()) and (peopleForm1.is_valid() or peopleForm2.is_valid())
and servicesForm.is_valid() and servicesForm.is_valid()
): # and captchaForm.is_valid(): and captchaForm.is_valid()
):
with transaction.atomic(): with transaction.atomic():
membershipForm = ChildrenForm({**post_data}) logging.debug("starting to save")
membership = membershipForm.save(commit=False) try:
membership.save() membership = Membership.objects.create()
if peopleForm1.is_valid(): if peopleForm1.is_valid():
people1_obj = peopleForm1.save(commit=False) people1_obj = peopleForm1.save(commit=False)
people1_obj.membership = membership people1_obj.membership = membership
people1_obj.save() people1_obj.save()
if peopleForm2.is_valid(): if peopleForm2.is_valid():
people2_obj = peopleForm2.save(commit=False) people2_obj = peopleForm2.save(commit=False)
people2_obj.membership = membership people2_obj.membership = membership
people2_obj.save() people2_obj.save()
committee_obj = committeeForm.save(commit=False) committee_obj = committeeForm.save(commit=False)
committee_obj.membership = membership committee_obj.membership = membership
committee_obj.save() committee_obj.save()
services_obj = servicesForm.save(commit=False) services_obj = servicesForm.save(commit=False)
services_obj.membership = membership services_obj.membership = membership
services_obj.save() services_obj.save()
address_obj = addressForm.save(commit=False) address_obj = addressForm.save(commit=False)
address_obj.membership = membership address_obj.membership = membership
address_obj.save() address_obj.save()
except Exception as e:
logging.error(e)
return redirect("index") return redirect("index2")
else: else:
logging.warning("Some field is not valid")
return render( return render(
request, request,
"schasite/membership_form2.html", "schasite/membership_form2.html",
{ {
"membershipForm": ChildrenForm,
"peopleForm1": peopleForm1, "peopleForm1": peopleForm1,
"peopleForm2": peopleForm2, "peopleForm2": peopleForm2,
"addressForm": addressForm, "addressForm": addressForm,
"committeeForm": committeeForm, "committeeForm": committeeForm,
"servicesForm": servicesForm, "servicesForm": servicesForm,
# 'captchaForm': captchaForm, 'captchaForm': captchaForm,
}, },
) )
else: else:
@@ -256,12 +319,11 @@ def membership_form2(request):
request, request,
"schasite/membership_form2.html", "schasite/membership_form2.html",
{ {
"membershipForm": ChildrenForm(),
"peopleForm1": PeopleForm(prefix="person1"), "peopleForm1": PeopleForm(prefix="person1"),
"peopleForm2": PeopleForm(prefix="person2"), "peopleForm2": PeopleForm(prefix="person2"),
"committeeForm": CommitteeForm(), "committeeForm": CommitteeForm(),
"servicesForm": ServicesForm(), "servicesForm": ServicesForm(),
# 'captchaForm': CaptchaForm(), 'captchaForm': CaptchaForm(),
"addressForm": AddressForm( "addressForm": AddressForm(
initial={ initial={
"city": "Wheaton", "city": "Wheaton",
@@ -415,3 +477,102 @@ def membership_form(request):
def scha_board(request): def scha_board(request):
return render(request, "schasite/scha_board.html", {}) return render(request, "schasite/scha_board.html", {})
def login(request):
if request.method == "POST":
username = request.POST["username"]
password = request.POST["password"]
user = authenticate(request, username=username, password=password)
if user is not None:
return redirect('dashboard')
else:
return render(request, "schasite/signin.html",{'error_msg': "Invalid username/password"})
else:
return render(request, "schasite/signin.html",{})
def logout(request):
if request.method == "POST":
logout(request)
return redirect('index2')
def password_reset(request):
if request.method == "POST":
raise NotImplementedError()
else:
return render(request, "schasite/password_reset.html",{})
def set_password(request):
if request.method == "POST":
raise NotImplementedError()
return redirect('login')
else:
return render(request, "schasite/set_password.html",{})
def signup(request):
if request.method == "POST":
raise NotImplementedError()
return redirect('login')
else:
return render(request, "schasite/signup.html",{})
@login_required(login_url="/login")
def member_directory(request):
if request.method == "POST":
# these should be filters
raise NotImplementedError()
else:
members = MembershipPerson.objects.all()
return render(request, "schasite/member_directory.html",{'members':members})
@login_required(login_url="/login")
def member_dashboard(request):
current_member = CommunityMember.objects.get(user=request.user)
if request.method == "POST":
raise NotImplementedError()
else:
recent_posts = CommunityPost.objects.all()[:5]
member_recent_posts = CommunityPost.objects.filter(author=current_member)[:5]
return render(request, "schasite/member_dashboard.html",{"recent_posts":recent_posts, "member_recent_posts": member_recent_posts, "current_member": current_member})
@login_required(login_url="/login")
def member_posts(request):
current_member = CommunityMember.objects.get(user=request.user)
if request.method == "POST":
raise NotImplementedError()
else:
posts = CommunityPost.objects.all()[:5]
return render(request, "schasite/member_posts.html",{"posts":posts})
@login_required(login_url="/login")
def member_posts_create(request):
current_member = CommunityMember.objects.get(user=request.user)
if request.method == "POST":
community_post_form = CommunityPostForm(request.POST)
if community_post_form.is_valid():
community_post_form.save()
return render(request, "schasite/member_posts.html",{})
else:
print(f'Error creating the post: {community_post_form.errors}')
return render(request, "schasite/member_posts_create.html",{"community_post": community_post_form})
else:
return render(request, "schasite/member_posts_create.html",{"community_post": CommunityPostForm()})
@login_required(login_url="/login")
def member_posts_detail(request, post_id):
current_member = CommunityMember.objects.get(user=request.user)
if request.method == "POST":
raise NotImplementedError()
else:
return render(request, "schasite/member_posts_detail.html",{})
#@login_required(login_url="/login")
def profile(request):
#current_member = CommunityMember.objects.get(user=request.user)
if request.method == "POST":
raise NotImplementedError()
else:
return render(request, "schasite/profile.html",{})