Compare commits

..

No commits in common. "11ac82d6f90b070f0a8f43549d99abc0ffe087bf" and "38c67b4a27d0ba5c55752ea91ed5a3cb1ade55e7" have entirely different histories.

12 changed files with 12 additions and 422 deletions

View File

@ -139,9 +139,9 @@ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = os.getenv('SMTP_HOST', 'smtp.hostinger.com')
EMAIL_PORT = int(os.getenv('SMTP_PORT', 465))
EMAIL_USE_SSL = True
EMAIL_HOST_USER = os.getenv('SMTP_USERNAME', 'admin@attunehearttherapy.com')
EMAIL_HOST_USER = os.getenv('SMTP_USERNAME', 'hello@attunehearttherapy.com')
EMAIL_HOST_PASSWORD = os.getenv('SMTP_PASSWORD')
DEFAULT_FROM_EMAIL = os.getenv('SMTP_FROM', 'admin@attunehearttherapy.com')
DEFAULT_FROM_EMAIL = os.getenv('SMTP_FROM', 'hello@attunehearttherapy.com')
REST_FRAMEWORK = {

View File

@ -8,24 +8,6 @@ def api_root(request, format=None):
base_url = request.build_absolute_uri('/api/')
endpoints = {
'contact': {
'description': 'Contact form submission endpoint',
'base_path': '/api/auth/',
'endpoints': {
'contact': {
'description': 'Submit a contact form',
'url': request.build_absolute_uri('/api/auth/contact/'),
'methods': ['POST'],
'required_fields': ['name', 'email', 'phone', 'message'],
'example_request': {
'name': 'John Doe',
'email': 'n8E5I@example.com',
'phone': '+1234567890',
'message': 'Hello, how can I help you?'
}
}
},
},
'authentication': {
'description': 'User authentication and management endpoints',
'base_path': '/api/auth/',

View File

@ -1,18 +0,0 @@
# Generated by Django 5.2.8 on 2025-11-28 15:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('meetings', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='appointmentrequest',
name='jitsi_room_id',
field=models.CharField(blank=True, default=None, help_text='Jitsi room ID', max_length=100, null=True, unique=True),
),
]

View File

@ -1,105 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Contact Form Submission</title>
</head>
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f3f4f6;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f3f4f6; padding: 40px 0;">
<tr>
<td align="center">
<table width="600" cellpadding="0" cellspacing="0" style="background-color: #ffffff; border-radius: 12px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">
<tr>
<td style="background: linear-gradient(135deg, #f43f5e 0%, #ec4899 100%); padding: 40px; border-radius: 12px 12px 0 0; text-align: center;">
<h1 style="margin: 0; color: #ffffff; font-size: 28px; font-weight: 700;">
🔔 New Contact Message
</h1>
<p style="margin: 10px 0 0 0; color: #fce7f3; font-size: 16px;">
Someone just reached out to you
</p>
</td>
</tr>
<tr>
<td style="padding: 40px;">
<table width="100%" cellpadding="0" cellspacing="0" style="margin-bottom: 30px;">
<tr>
<td colspan="2" style="padding-bottom: 20px;">
<h2 style="margin: 0; color: #111827; font-size: 20px; font-weight: 600;">
Contact Information
</h2>
</td>
</tr>
<tr>
<td style="padding: 12px 0; width: 120px; color: #6b7280; font-size: 14px; font-weight: 600;">
Name:
</td>
<td style="padding: 12px 0; color: #111827; font-size: 14px;">
{{ contact_message.name }}
</td>
</tr>
<tr style="border-top: 1px solid #e5e7eb;">
<td style="padding: 12px 0; width: 120px; color: #6b7280; font-size: 14px; font-weight: 600;">
Email:
</td>
<td style="padding: 12px 0;">
<a href="mailto:{{ contact_message.email }}" style="color: #f43f5e; text-decoration: none; font-size: 14px;">
{{ contact_message.email }}
</a>
</td>
</tr>
{% if contact_message.phone %}
<tr style="border-top: 1px solid #e5e7eb;">
<td style="padding: 12px 0; width: 120px; color: #6b7280; font-size: 14px; font-weight: 600;">
Phone:
</td>
<td style="padding: 12px 0;">
<a href="tel:{{ contact_message.phone }}" style="color: #f43f5e; text-decoration: none; font-size: 14px;">
{{ contact_message.phone }}
</a>
</td>
</tr>
{% endif %}
<tr style="border-top: 1px solid #e5e7eb;">
<td style="padding: 12px 0; width: 120px; color: #6b7280; font-size: 14px; font-weight: 600;">
Received:
</td>
<td style="padding: 12px 0; color: #111827; font-size: 14px;">
{{ contact_message.created_at|date:"F d, Y" }} at {{ contact_message.created_at|time:"h:i A" }}
</td>
</tr>
</table>
<div style="margin-bottom: 30px;">
<h2 style="margin: 0 0 15px 0; color: #111827; font-size: 20px; font-weight: 600;">
Message
</h2>
<div style="background-color: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 20px;">
<p style="margin: 0; color: #374151; font-size: 14px; line-height: 1.6; white-space: pre-wrap;">{{ contact_message.message }}</p>
</div>
</div>
<div style="text-align: center; margin-top: 30px;">
<a href="mailto:{{ contact_message.email }}" style="display: inline-block; background: linear-gradient(135deg, #f43f5e 0%, #ec4899 100%); color: #ffffff; text-decoration: none; padding: 14px 32px; border-radius: 8px; font-weight: 600; font-size: 15px; box-shadow: 0 4px 6px rgba(244, 63, 94, 0.3);">
Reply to {{ contact_message.name }}
</a>
</div>
</td>
</tr>
<tr>
<td style="background-color: #f9fafb; padding: 30px; border-radius: 0 0 12px 12px; text-align: center; border-top: 1px solid #e5e7eb;">
<p style="margin: 0; color: #6b7280; font-size: 13px; line-height: 1.6;">
This is an automated notification from your contact form.<br>
Please respond to the sender as soon as possible.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -1,80 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Thank You for Contacting Us</title>
</head>
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f3f4f6;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f3f4f6; padding: 40px 0;">
<tr>
<td align="center">
<table width="600" cellpadding="0" cellspacing="0" style="background-color: #ffffff; border-radius: 12px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);">
<tr>
<td style="background: linear-gradient(135deg, #f43f5e 0%, #ec4899 100%); padding: 40px; border-radius: 12px 12px 0 0; text-align: center;">
<h1 style="margin: 0; color: #ffffff; font-size: 28px; font-weight: 700;">
✨ Thank You!
</h1>
<p style="margin: 10px 0 0 0; color: #fce7f3; font-size: 16px;">
We've received your message
</p>
</td>
</tr>
<tr>
<td style="padding: 40px;">
<div style="text-align: center; margin-bottom: 30px;">
<div style="display: inline-block; background-color: #fef2f2; border-radius: 50%; padding: 20px; margin-bottom: 20px;">
<span style="font-size: 48px;"></span>
</div>
</div>
<h2 style="margin: 0 0 20px 0; color: #111827; font-size: 22px; font-weight: 600; text-align: center;">
Hi {{ contact_message.name }},
</h2>
<p style="margin: 0 0 20px 0; color: #374151; font-size: 16px; line-height: 1.6; text-align: center;">
Thank you for reaching out to us. We've received your message and our team will review it shortly. We typically respond within 24-48 hours.
</p>
<div style="background-color: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 25px; margin: 30px 0;">
<h3 style="margin: 0 0 15px 0; color: #111827; font-size: 16px; font-weight: 600;">
Your Message:
</h3>
<p style="margin: 0; color: #6b7280; font-size: 14px; line-height: 1.6; white-space: pre-wrap;">{{ contact_message.message }}</p>
</div>
<div style="background-color: #fef2f2; border-left: 4px solid #f43f5e; padding: 20px; border-radius: 6px; margin: 30px 0;">
<p style="margin: 0 0 10px 0; color: #991b1b; font-weight: 600; font-size: 14px;">
📧 We'll reply to:
</p>
<p style="margin: 0; color: #b91c1c; font-size: 14px;">
{{ contact_message.email }}
</p>
</div>
<p style="margin: 30px 0 0 0; color: #6b7280; font-size: 14px; line-height: 1.6; text-align: center;">
If you have any urgent questions in the meantime, please don't hesitate to reach out to us directly.
</p>
</td>
</tr>
<tr>
<td style="background-color: #f9fafb; padding: 30px; border-radius: 0 0 12px 12px; text-align: center; border-top: 1px solid #e5e7eb;">
<p style="margin: 0 0 15px 0; color: #111827; font-size: 14px; font-weight: 600;">
Need immediate assistance?
</p>
<p style="margin: 0; color: #6b7280; font-size: 13px; line-height: 1.6;">
Email us at <a href="mailto:{{support_email}}" style="color: #f43f5e; text-decoration: none;">{{support_email}}</a><br>
or call us at <a href="tel:+1754816-2311" style="color: #f43f5e; text-decoration: none;">+1 (754) 816-2311</a>
</p>
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #e5e7eb;">
<p style="margin: 0 0 10px 0; color: #9ca3af; font-size: 12px;">
© {{ current_year }} {{ company_name }}. All rights reserved.
</p>
</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -1,5 +1,7 @@
from django.contrib import admin
from .models import CustomUser, UserProfile, ContactMessage
from .models import CustomUser, UserProfile
# Register your models here.
@admin.register(CustomUser)
class UserAdmin(admin.ModelAdmin):
@ -15,41 +17,3 @@ class UserProfileAdmin(admin.ModelAdmin):
ordering = ('user__email',)
@admin.register(ContactMessage)
class ContactMessageAdmin(admin.ModelAdmin):
list_display = ['name', 'email', 'phone', 'created_at', 'is_read', 'is_responded']
list_filter = ['is_read', 'is_responded', 'created_at']
search_fields = ['name', 'email', 'phone', 'message']
readonly_fields = ['created_at']
date_hierarchy = 'created_at'
fieldsets = (
('Contact Information', {
'fields': ('name', 'email', 'phone')
}),
('Message', {
'fields': ('message',)
}),
('Status', {
'fields': ('is_read', 'is_responded', 'created_at')
}),
)
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.select_related()
actions = ['mark_as_read', 'mark_as_responded']
def mark_as_read(self, request, queryset):
updated = queryset.update(is_read=True)
self.message_user(request, f'{updated} message(s) marked as read.')
mark_as_read.short_description = "Mark selected as read"
def mark_as_responded(self, request, queryset):
updated = queryset.update(is_responded=True)
self.message_user(request, f'{updated} message(s) marked as responded.')
mark_as_responded.short_description = "Mark selected as responded"

View File

@ -1,31 +0,0 @@
# Generated by Django 5.2.8 on 2025-11-28 15:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='ContactMessage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255)),
('email', models.EmailField(max_length=254)),
('phone', models.CharField(blank=True, max_length=20)),
('message', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('is_read', models.BooleanField(default=False)),
('is_responded', models.BooleanField(default=False)),
],
options={
'verbose_name': 'Contact Message',
'verbose_name_plural': 'Contact Messages',
'ordering': ['-created_at'],
},
),
]

View File

@ -37,22 +37,4 @@ class UserProfile(models.Model):
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.user.email} Profile"
class ContactMessage(models.Model):
name = models.CharField(max_length=255)
email = models.EmailField()
phone = models.CharField(max_length=20, blank=True)
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
is_read = models.BooleanField(default=False)
is_responded = models.BooleanField(default=False)
class Meta:
ordering = ['-created_at']
verbose_name = 'Contact Message'
verbose_name_plural = 'Contact Messages'
def __str__(self):
return f"{self.name} - {self.email} - {self.created_at.strftime('%Y-%m-%d')}"
return f"{self.user.email} Profile"

View File

@ -1,6 +1,6 @@
from rest_framework import serializers
from django.contrib.auth.password_validation import validate_password
from .models import CustomUser, UserProfile, ContactMessage
from .models import CustomUser, UserProfile
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
@ -53,23 +53,4 @@ class ResetPasswordSerializer(serializers.Serializer):
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = CustomUser
fields = ('id', 'email', 'first_name', 'last_name', 'phone_number', 'isVerified', 'date_joined', 'last_login', 'is_staff', 'is_superuser', 'is_active')
from rest_framework import serializers
from .models import ContactMessage
class ContactMessageSerializer(serializers.ModelSerializer):
class Meta:
model = ContactMessage
fields = ['id', 'name', 'email', 'phone', 'message', 'created_at']
read_only_fields = ['id', 'created_at']
def validate_name(self, value):
if len(value.strip()) < 2:
raise serializers.ValidationError("Name must be at least 2 characters long.")
return value.strip()
def validate_message(self, value):
if len(value.strip()) < 10:
raise serializers.ValidationError("Message must be at least 10 characters long.")
return value.strip()
fields = ('id', 'email', 'first_name', 'last_name', 'phone_number', 'isVerified', 'date_joined', 'last_login', 'is_staff', 'is_superuser', 'is_active')

View File

@ -3,8 +3,6 @@ from rest_framework_simplejwt.views import TokenRefreshView
from . import views
urlpatterns = [
path('contact/', views.ContactMessageView.as_view(), name='contact-message'),
path('register/', views.register_user, name='register'),
path('login/', views.login_user, name='login'),

View File

@ -6,9 +6,6 @@ from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
import logging
logger = logging.getLogger(__name__)
def generate_otp():
return str(random.randint(100000, 999999))
@ -57,49 +54,4 @@ def send_otp_via_email(email, otp, user_name=None, context='registration'):
def is_otp_expired(otp_expiry):
if otp_expiry and timezone.now() < otp_expiry:
return False
return True
def send_email_notifications(contact_message):
try:
send_admin_notification(contact_message)
send_user_confirmation(contact_message)
except Exception as e:
logger.error(f"Error sending email notifications: {str(e)}")
def send_admin_notification(contact_message):
subject = f"New Contact Form Submission from {contact_message.name}"
html_content = render_to_string('emails/admin_contact_notification.html', {
'contact_message': contact_message
})
email = EmailMultiAlternatives(
subject=subject,
body=html_content,
from_email=settings.DEFAULT_FROM_EMAIL,
to=[settings.DEFAULT_FROM_EMAIL]
)
email.content_subtype = 'html'
email.send()
def send_user_confirmation(self, contact_message):
subject = "Thank you for contacting us"
html_content = render_to_string('emails/user_contact_confirmation.html', {
'contact_message': contact_message,
'company_name': 'Attune Heart Therapy',
'support_email': 'admin@attunehearttherapy.com',
'current_year': timezone.now().year,
})
email = EmailMultiAlternatives(
subject=subject,
body=html_content,
from_email=settings.DEFAULT_FROM_EMAIL,
to=[contact_message.email]
)
email.content_subtype = 'html'
email.send()
return True

View File

@ -1,51 +1,16 @@
from rest_framework import status, generics
from rest_framework.decorators import api_view, permission_classes
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny, IsAuthenticated, IsAdminUser
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth import authenticate
from .models import CustomUser, UserProfile, ContactMessage
from .serializers import UserRegistrationSerializer, UserSerializer, ResetPasswordSerializer, ForgotPasswordSerializer, VerifyPasswordResetOTPSerializer, ContactMessageSerializer
from .utils import send_otp_via_email, is_otp_expired, generate_otp,send_email_notifications
from .models import CustomUser, UserProfile
from .serializers import UserRegistrationSerializer, UserSerializer, ResetPasswordSerializer, ForgotPasswordSerializer, VerifyPasswordResetOTPSerializer
from .utils import send_otp_via_email, is_otp_expired, generate_otp
from django.utils import timezone
from datetime import timedelta
from rest_framework.reverse import reverse
from meetings.models import AppointmentRequest
import logging
logger = logging.getLogger(__name__)
class ContactMessageView(APIView):
def post(self, request):
serializer = ContactMessageSerializer(data=request.data)
if serializer.is_valid():
try:
contact_message = serializer.save()
send_email_notifications(contact_message)
return Response({
'success': True,
'message': 'Thank you for your message. We will get back to you soon!',
'data': serializer.data
}, status=status.HTTP_201_CREATED)
except Exception as e:
logger.error(f"Error processing contact form: {str(e)}")
return Response({
'success': False,
'message': 'There was an error processing your request. Please try again later.'
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response({
'success': False,
'message': 'Please check your input and try again.',
'errors': serializer.errors
}, status=status.HTTP_400_BAD_REQUEST)
@api_view(['POST'])