base setup
This commit is contained in:
0
at_django_boilerplate/communications/__init__.py
Executable file
0
at_django_boilerplate/communications/__init__.py
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6
at_django_boilerplate/communications/admin.py
Executable file
6
at_django_boilerplate/communications/admin.py
Executable file
@@ -0,0 +1,6 @@
|
||||
from django.contrib import admin
|
||||
from .models import FeedbackModel
|
||||
|
||||
|
||||
|
||||
admin.site.register(FeedbackModel)
|
||||
6
at_django_boilerplate/communications/apps.py
Executable file
6
at_django_boilerplate/communications/apps.py
Executable file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CommunicationsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'at_django_boilerplate.communications'
|
||||
@@ -0,0 +1,50 @@
|
||||
# Generated by Django 5.2.6 on 2025-12-29 05:20
|
||||
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AppointmentModel',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('full_name', models.CharField(max_length=255)),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('phone', models.CharField(max_length=20)),
|
||||
('meeting_with', models.CharField(choices=[('business-consultant', 'Meet to Lawyer'), ('compliance-specialist', 'Meet to CA'), ('incorporation-expert', 'Meet to Secretary')], max_length=50)),
|
||||
('appointment_datetime', models.DateTimeField()),
|
||||
('notes', models.TextField(blank=True, null=True)),
|
||||
('status', models.CharField(choices=[('pending', 'Pending'), ('contacted', 'Contacted'), ('in_process', 'In Process'), ('rescheduled', 'Rescheduled'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='pending', max_length=20)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='appointments', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-appointment_datetime'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='FeedbackModel',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('email', models.EmailField(blank=True, max_length=254, null=True)),
|
||||
('phone', models.CharField(blank=True, max_length=20, null=True)),
|
||||
('name', models.CharField(blank=True, max_length=255, null=True)),
|
||||
('subject', models.TextField(blank=True, null=True)),
|
||||
('message', models.TextField(blank=True, null=True)),
|
||||
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
|
||||
('to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='feedbacks', to=settings.AUTH_USER_MODEL)),
|
||||
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='feedback_from', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.2.6 on 2026-01-03 06:07
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('communications', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='feedbackmodel',
|
||||
name='is_junk',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 5.2.6 on 2026-01-03 06:18
|
||||
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('communications', '0002_feedbackmodel_is_junk'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='appointmentmodel',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='feedbackmodel',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False),
|
||||
),
|
||||
]
|
||||
0
at_django_boilerplate/communications/migrations/__init__.py
Executable file
0
at_django_boilerplate/communications/migrations/__init__.py
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
63
at_django_boilerplate/communications/models.py
Executable file
63
at_django_boilerplate/communications/models.py
Executable file
@@ -0,0 +1,63 @@
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from at_django_boilerplate.utils.mixins import UUIDMixin
|
||||
|
||||
|
||||
|
||||
class FeedbackModel(UUIDMixin):
|
||||
user = models.ForeignKey('accounts.CustomUser',related_name='feedback_from',null=True,blank=True,on_delete=models.DO_NOTHING)
|
||||
email = models.EmailField(null=True,blank=True)
|
||||
phone = models.CharField(max_length=20, null=True, blank=True) # Corrected
|
||||
name = models.CharField(max_length=255, blank=True, null=True)
|
||||
to = models.ForeignKey('accounts.CustomUser',related_name='feedbacks',null=True,blank=True,on_delete=models.DO_NOTHING)
|
||||
is_junk = models.BooleanField(default= False)
|
||||
|
||||
subject = models.TextField(null=True,blank=True)
|
||||
message = models.TextField(null=True,blank=True)
|
||||
|
||||
created_at = models.DateTimeField(default=timezone.now)
|
||||
|
||||
|
||||
# communications/models.py
|
||||
|
||||
class AppointmentModel(UUIDMixin):
|
||||
MEETING_CHOICES = (
|
||||
('business-consultant', 'Meet to Lawyer'),
|
||||
('compliance-specialist', 'Meet to CA'),
|
||||
('incorporation-expert', 'Meet to Secretary'),
|
||||
)
|
||||
|
||||
STATUS_CHOICES = (
|
||||
('pending', 'Pending'),
|
||||
('contacted', 'Contacted'),
|
||||
('in_process', 'In Process'),
|
||||
('rescheduled', 'Rescheduled'),
|
||||
('completed', 'Completed'),
|
||||
('cancelled', 'Cancelled'),
|
||||
)
|
||||
|
||||
user = models.ForeignKey(
|
||||
'accounts.CustomUser',
|
||||
related_name='appointments',
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.DO_NOTHING
|
||||
)
|
||||
full_name = models.CharField(max_length=255)
|
||||
email = models.EmailField()
|
||||
phone = models.CharField(max_length=20)
|
||||
meeting_with = models.CharField(max_length=50, choices=MEETING_CHOICES)
|
||||
appointment_datetime = models.DateTimeField()
|
||||
notes = models.TextField(blank=True, null=True)
|
||||
|
||||
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='pending')
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return f"APP-{self.pk:06d} - {self.full_name}"
|
||||
|
||||
class Meta:
|
||||
ordering = ['-appointment_datetime']
|
||||
437
at_django_boilerplate/communications/templates/book_appointment.html
Executable file
437
at_django_boilerplate/communications/templates/book_appointment.html
Executable file
@@ -0,0 +1,437 @@
|
||||
{% extends 'public_base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Book an Appointment - RegisterYourStartup.com{% endblock %}
|
||||
{% block meta_description %}Schedule a consultation with our business experts. Book an appointment for personalized guidance on incorporation, compliance, and business growth strategies.{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
/* Glassmorphism Effect */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar { width: 8px; }
|
||||
::-webkit-scrollbar-track { background: #f1f1f1; }
|
||||
::-webkit-scrollbar-thumb { background: #6b7280; border-radius: 4px; }
|
||||
/* Smooth animations */
|
||||
.hover-lift { transition: transform 0.3s ease, box-shadow 0.3s ease; }
|
||||
.hover-lift:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); }
|
||||
/* Gradient text */
|
||||
.gradient-text { background: linear-gradient(90deg, #4f46e5, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
||||
|
||||
/* Appointment Form Styles */
|
||||
:root {
|
||||
--bg-color: #f8fafc;
|
||||
--text-color: #1f2937;
|
||||
--accent-color: #4f46e5;
|
||||
--input-border: #4f46e5;
|
||||
--button-bg: #4f46e5;
|
||||
--button-text: #ffffff;
|
||||
--card-bg: #ffffff;
|
||||
}
|
||||
|
||||
.appointment-form-section {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
padding: 3rem 1rem;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.appointment-form-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 3rem;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.appointment-form-wrapper {
|
||||
flex: 1;
|
||||
min-width: 400px;
|
||||
background: var(--card-bg);
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.appointment-form-wrapper label {
|
||||
color: var(--text-color);
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.appointment-form-wrapper input,
|
||||
.appointment-form-wrapper textarea,
|
||||
.appointment-form-wrapper select {
|
||||
border: 1px solid var(--input-border);
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem;
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
background: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.appointment-form-wrapper input:focus,
|
||||
.appointment-form-wrapper textarea:focus,
|
||||
.appointment-form-wrapper select:focus {
|
||||
outline: none;
|
||||
border-color: #7c3aed;
|
||||
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
|
||||
}
|
||||
|
||||
.appointment-form-wrapper input::placeholder,
|
||||
.appointment-form-wrapper textarea::placeholder {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.appointment-form-wrapper .btn-submit {
|
||||
background: linear-gradient(90deg, #4f46e5, #7c3aed);
|
||||
color: var(--button-text);
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
width: 100%;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.appointment-form-wrapper .btn-submit:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(79, 70, 229, 0.3);
|
||||
}
|
||||
|
||||
.benefits-wrapper {
|
||||
flex: 1;
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.appointment-heading {
|
||||
font-size: 3.5rem;
|
||||
font-weight: bold;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.appointment-subheading {
|
||||
font-size: 1.2rem;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 2rem;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.appointment-form-container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.appointment-heading {
|
||||
font-size: 2.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.appointment-form-wrapper,
|
||||
.benefits-wrapper {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #dc2626;
|
||||
font-size: 0.875rem;
|
||||
margin-top: -0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Benefits Section */
|
||||
.benefits-section {
|
||||
background: linear-gradient(135deg, #4f46e5, #7c3aed);
|
||||
color: white;
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.benefit-item {
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.benefit-icon {
|
||||
margin-right: 15px;
|
||||
font-size: 1.2rem;
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.benefit-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 5px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.benefit-description {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.success-message {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
padding: 1.5rem;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #a7f3d0;
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
font-size: 1.1rem;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="py-16 md:py-24 bg-[#B6C3FD]">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex flex-col md:flex-row items-center">
|
||||
<div class="md:w-1/2 mb-10 md:mb-0">
|
||||
<h1 class="pt-10 text-4xl md:text-5xl font-extrabold text-gray-900 mb-4 leading-tight">
|
||||
Book an <span class="gradient-text">Appointment</span>
|
||||
</h1>
|
||||
<p class="text-lg text-gray-700 mb-8 max-w-xl">
|
||||
Schedule a consultation with our business experts. Get personalized guidance on incorporation, compliance, and growth strategies tailored to your needs.
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
||||
<a href="#appointment-form" class="bg-indigo-600 text-white px-8 py-3 rounded-full font-medium hover:bg-indigo-700 transition text-center">Book Now</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:w-1/2 flex justify-center relative">
|
||||
<div class="w-80 h-80 bg-gradient-to-br from-indigo-200 to-purple-200 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-calendar-check text-6xl text-indigo-600"></i>
|
||||
</div>
|
||||
<div class="absolute -top-4 -right-4 w-40 h-40 bg-gradient-to-br from-pink-200 to-orange-200 rounded-full opacity-70"></div>
|
||||
<div class="absolute -bottom-4 -left-4 w-32 h-32 bg-gradient-to-br from-green-200 to-blue-200 rounded-full opacity-70"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Appointment Form Section -->
|
||||
<section id="appointment-form" class="appointment-form-section">
|
||||
<div class="appointment-form-container">
|
||||
|
||||
<!-- Django Messages (Success / Error) -->
|
||||
{% if messages %}
|
||||
<div class="w-full max-w-4xl mx-auto mb-8">
|
||||
{% for message in messages %}
|
||||
<div class="success-message">
|
||||
{{ message|safe }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<!-- Left: Appointment Form -->
|
||||
<div class="appointment-form-wrapper">
|
||||
<h2 class="text-2xl font-bold mb-6 text-center" style="color: var(--text-color);">Schedule Your Consultation</h2>
|
||||
<form method="post" class="vertical-form">
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- All your form fields unchanged -->
|
||||
<div class="form-group">
|
||||
<label for="fullName">Full Name</label>
|
||||
<input id="fullName" name="fullName" class="form-control" placeholder="Enter your full name" required type="text">
|
||||
<span id="fullName-error" class="error"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input id="email" name="email" class="form-control" placeholder="Enter your email" required type="email">
|
||||
<span id="email-error" class="error"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="phone">Phone Number</label>
|
||||
<input id="phone" name="phone" class="form-control" placeholder="Enter your phone number" required type="tel">
|
||||
<span id="phone-error" class="error"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="meetingWith">Meet with</label>
|
||||
<select id="meetingWith" name="meetingWith" required>
|
||||
<option value="">Select an expert</option>
|
||||
<option value="business-consultant">Lawyer (Business Consultation)</option>
|
||||
<option value="compliance-specialist">Chartered Accountant (CA)</option>
|
||||
<option value="incorporation-expert">Company Secretary</option>
|
||||
</select>
|
||||
<span id="meetingWith-error" class="error"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="appointmentDateTime">Appointment Date & Time</label>
|
||||
<input type="datetime-local" id="appointmentDateTime" name="appointmentDateTime" required>
|
||||
<span id="appointmentDateTime-error" class="error"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="notes">Notes (Optional)</label>
|
||||
<textarea id="notes" name="notes" rows="4" class="form-control" placeholder="Any specific questions or topics you'd like to discuss?"></textarea>
|
||||
</div>
|
||||
|
||||
<input class="btn btn-submit" type="submit" value="BOOK APPOINTMENT">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Right: Benefits -->
|
||||
<div class="benefits-wrapper">
|
||||
<h1 class="appointment-heading">
|
||||
Book an appointment with us
|
||||
</h1>
|
||||
<p class="appointment-subheading">
|
||||
Fill out the form to connect with us. During this session, our experts will:
|
||||
</p>
|
||||
|
||||
<div id="benefits" class="benefits-section">
|
||||
<div class="benefit-item">
|
||||
<div class="benefit-icon">+</div>
|
||||
<div>
|
||||
<div class="benefit-title">Walk you through our services</div>
|
||||
<p class="benefit-description">We provide solutions tailored to your business needs</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="benefit-item">
|
||||
<div class="benefit-icon">+</div>
|
||||
<div>
|
||||
<div class="benefit-title">Answer your specific questions</div>
|
||||
<p class="benefit-description">Get actionable insights for your business</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="benefit-item">
|
||||
<div class="benefit-icon">+</div>
|
||||
<div>
|
||||
<div class="benefit-title">Expert Guidance</div>
|
||||
<p class="benefit-description">Receive personalized advice from specialists in incorporation, compliance, and business growth</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="benefit-item">
|
||||
<div class="benefit-icon">+</div>
|
||||
<div>
|
||||
<div class="benefit-title">Time-Saving Efficiency</div>
|
||||
<p class="benefit-description">Streamlined consultations help you get clear answers quickly</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="benefit-item">
|
||||
<div class="benefit-icon">+</div>
|
||||
<div>
|
||||
<div class="benefit-title">Comprehensive Support</div>
|
||||
<p class="benefit-description">From starting a business to managing operations globally, we've got you covered</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section class="py-16 bg-gradient-to-r from-indigo-500 to-purple-600">
|
||||
<div class="container mx-auto px-4 text-center">
|
||||
<h2 class="text-3xl md:text-4xl font-bold text-white mb-4">Ready to Grow Your Business?</h2>
|
||||
<p class="text-indigo-100 text-lg mb-8 max-w-2xl mx-auto">Book a consultation today and let our expert team guide you through seamless business setup and expansion</p>
|
||||
<div class="flex flex-col sm:flex-row justify-center space-y-4 sm:space-y-0 sm:space-x-4">
|
||||
<a href="{% url 'contact_us_form' %}" class="bg-white text-indigo-600 px-8 py-3 rounded-full font-medium hover:bg-indigo-50 transition">Contact Us</a>
|
||||
<a href="tel:+9192204-33466" class="border border-white text-white px-8 py-3 rounded-full font-medium hover:bg-white hover:text-indigo-600 transition">Call: +91 92204-33466</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
// Client-side form validation
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.querySelector('form');
|
||||
const fullNameInput = document.getElementById('fullName');
|
||||
const emailInput = document.getElementById('email');
|
||||
const phoneInput = document.getElementById('phone');
|
||||
const meetingWithInput = document.getElementById('meetingWith');
|
||||
const appointmentDateTimeInput = document.getElementById('appointmentDateTime');
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
let isValid = true;
|
||||
|
||||
// Clear previous errors
|
||||
document.querySelectorAll('.error').forEach(error => error.textContent = '');
|
||||
|
||||
// Validate full name
|
||||
if (!fullNameInput.value.trim()) {
|
||||
document.getElementById('fullName-error').textContent = 'Full name is required';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Validate email
|
||||
if (!emailInput.value.trim()) {
|
||||
document.getElementById('email-error').textContent = 'Email is required';
|
||||
isValid = false;
|
||||
} else {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(emailInput.value)) {
|
||||
document.getElementById('email-error').textContent = 'Please enter a valid email address';
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate phone
|
||||
if (!phoneInput.value.trim()) {
|
||||
document.getElementById('phone-error').textContent = 'Phone number is required';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Validate meeting with
|
||||
if (!meetingWithInput.value) {
|
||||
document.getElementById('meetingWith-error').textContent = 'Please select who you want to meet with';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Validate appointment date & time
|
||||
if (!appointmentDateTimeInput.value) {
|
||||
document.getElementById('appointmentDateTime-error').textContent = 'Please select appointment date and time';
|
||||
isValid = false;
|
||||
} else {
|
||||
const selectedDateTime = new Date(appointmentDateTimeInput.value);
|
||||
const now = new Date();
|
||||
if (selectedDateTime <= now) {
|
||||
document.getElementById('appointmentDateTime-error').textContent = 'Please select a future date and time';
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
518
at_django_boilerplate/communications/templates/contact_us_form.html
Executable file
518
at_django_boilerplate/communications/templates/contact_us_form.html
Executable file
@@ -0,0 +1,518 @@
|
||||
{% extends 'public_base.html' %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Contact Us - RegisterYourStartup.com{% endblock %}
|
||||
{% block meta_description %}Get in touch with RegisterYourStartup.com. Contact our global offices in India, UAE, USA, UK, Saudi Arabia, Malaysia, Singapore, Indonesia, Kenya, and Bangalore.{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
/* Glassmorphism Effect */
|
||||
.glass {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
/* Custom Scrollbar */
|
||||
::-webkit-scrollbar { width: 8px; }
|
||||
::-webkit-scrollbar-track { background: #f1f1f1; }
|
||||
::-webkit-scrollbar-thumb { background: #6b7280; border-radius: 4px; }
|
||||
/* Smooth animations */
|
||||
.hover-lift { transition: transform 0.3s ease, box-shadow 0.3s ease; }
|
||||
.hover-lift:hover { transform: translateY(-5px); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); }
|
||||
/* Gradient text */
|
||||
.gradient-text { background: linear-gradient(90deg, #4f46e5, #7c3aed); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
||||
|
||||
/* Contact Form Styles - Using template colors */
|
||||
:root {
|
||||
--bg-color: #f8fafc;
|
||||
--text-color: #1f2937;
|
||||
--accent-color: #4f46e5;
|
||||
--input-border: #4f46e5;
|
||||
--button-bg: #4f46e5;
|
||||
--button-text: #ffffff;
|
||||
--card-bg: #ffffff;
|
||||
}
|
||||
|
||||
.contact-form-section {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-color);
|
||||
padding: 3rem 1rem;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contact-form-container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 3rem;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.contact-form-wrapper {
|
||||
flex: 1;
|
||||
min-width: 400px;
|
||||
background: var(--card-bg);
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.contact-form-wrapper label {
|
||||
color: var(--text-color);
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.contact-form-wrapper input,
|
||||
.contact-form-wrapper textarea {
|
||||
border: 1px solid var(--input-border);
|
||||
border-radius: 8px;
|
||||
padding: 0.75rem;
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
background: var(--card-bg);
|
||||
color: var(--text-color);
|
||||
font-size: 1rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.contact-form-wrapper input:focus,
|
||||
.contact-form-wrapper textarea:focus {
|
||||
outline: none;
|
||||
border-color: #7c3aed;
|
||||
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.1);
|
||||
}
|
||||
|
||||
.contact-form-wrapper input::placeholder,
|
||||
.contact-form-wrapper textarea::placeholder {
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.contact-form-wrapper .btn-submit {
|
||||
background: linear-gradient(90deg, #4f46e5, #7c3aed);
|
||||
color: var(--button-text);
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
width: 100%;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.contact-form-wrapper .btn-submit:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(79, 70, 229, 0.3);
|
||||
}
|
||||
|
||||
.contact-info-wrapper {
|
||||
flex: 1;
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.contact-heading {
|
||||
font-size: 3.5rem;
|
||||
font-weight: bold;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.contact-subheading {
|
||||
font-size: 1.2rem;
|
||||
color: var(--text-color);
|
||||
margin-bottom: 2rem;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.contact-form-container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.contact-heading {
|
||||
font-size: 2.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.contact-form-wrapper,
|
||||
.contact-info-wrapper {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #dc2626;
|
||||
font-size: 0.875rem;
|
||||
margin-top: -0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Offices Grid */
|
||||
.offices-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.office-card {
|
||||
background: var(--card-bg);
|
||||
padding: 1.5rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
border-left: 4px solid var(--accent-color);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.office-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.office-card h3 {
|
||||
color: var(--text-color);
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.office-card h3 i {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
.office-card p {
|
||||
margin: 0.5rem 0;
|
||||
color: #6b7280;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.contact-details {
|
||||
background: var(--card-bg);
|
||||
padding: 2rem;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.contact-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.contact-item:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.contact-icon {
|
||||
background: linear-gradient(90deg, #4f46e5, #7c3aed);
|
||||
color: white;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 1rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.contact-content h4 {
|
||||
color: var(--text-color);
|
||||
margin-bottom: 0.5rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.contact-content p {
|
||||
color: #6b7280;
|
||||
margin: 0.25rem 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: var(--text-color);
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="py-16 md:py-24 bg-[#B6C3FD]">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex flex-col md:flex-row items-center">
|
||||
<div class="md:w-1/2 mb-10 md:mb-0">
|
||||
<h1 class=" pt-10 text-4xl md:text-5xl font-extrabold text-gray-900 mb-4 leading-tight">
|
||||
Contact <span class="gradient-text">Us</span>
|
||||
</h1>
|
||||
<p class="text-lg text-gray-700 mb-8 max-w-xl">
|
||||
Get in touch with our global team of experts. We're here to help you with business incorporation, compliance, and expansion worldwide.
|
||||
</p>
|
||||
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
||||
<a href="#offices" class="border border-indigo-600 text-indigo-600 px-8 py-3 rounded-full font-medium hover:bg-indigo-50 transition text-center">Our Offices</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="md:w-1/2 flex justify-center relative">
|
||||
<div class="w-80 h-80 bg-gradient-to-br from-indigo-200 to-purple-200 rounded-full flex items-center justify-center">
|
||||
<i class="fas fa-envelope text-6xl text-indigo-600"></i>
|
||||
</div>
|
||||
<div class="absolute -top-4 -right-4 w-40 h-40 bg-gradient-to-br from-pink-200 to-orange-200 rounded-full opacity-70"></div>
|
||||
<div class="absolute -bottom-4 -left-4 w-32 h-32 bg-gradient-to-br from-green-200 to-blue-200 rounded-full opacity-70"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Contact Form Section -->
|
||||
<section id="contact-form" class="contact-form-section">
|
||||
<div class="contact-form-container">
|
||||
|
||||
<!-- Left: Contact Form -->
|
||||
<div class="contact-form-wrapper">
|
||||
<h2 class="text-2xl font-bold mb-6 text-center" style="color: var(--text-color);">Get In Touch</h2>
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-success w-100 text-center mt-4 mb-4" style="background: #d1fae5; color: #065f46; padding: 1.2rem; border-radius: 8px; border: 1px solid #a7f3d0; font-size: 1.1rem;">
|
||||
{{ message|safe }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<form method="post" class="vertical-form">
|
||||
{% csrf_token %}
|
||||
<label class="form-label" for="name">Your Name</label>
|
||||
<input id="name" name="name" class="form-control" placeholder="Enter your full name" required type="text"
|
||||
{% if user.is_authenticated %}value="{{ user.get_full_name }}"{% endif %}>
|
||||
<span id="name-error" class="error"></span>
|
||||
<label class="form-label" for="subject">Subject</label>
|
||||
<input id="subject" name="subject" class="form-control" placeholder="Enter your subject" required type="text">
|
||||
<span id="subject-error" class="error"></span>
|
||||
|
||||
<label class="form-label" for="message">Message</label>
|
||||
<textarea id="message" name="message" rows="6" class="form-control" placeholder="Enter your message" required></textarea>
|
||||
<span id="message-error" class="error"></span>
|
||||
|
||||
{% if not user.is_authenticated %}
|
||||
<label class="form-label" for="email">Email</label>
|
||||
<input id="email" name="email" class="form-control" placeholder="Enter your email" required type="email">
|
||||
<span id="email-error" class="error"></span>
|
||||
|
||||
<label class="form-label" for="phone">Phone (Optional)</label>
|
||||
<input id="phone" name="phone" class="form-control" placeholder="Enter your phone number" type="tel">
|
||||
<span id="phone-error" class="error"></span>
|
||||
{% endif %}
|
||||
|
||||
<input class="btn btn-submit" type="submit" value="Send Message">
|
||||
</form>
|
||||
|
||||
<!-- ✅ Success message below the form -->
|
||||
{% if feedback_submitted %}
|
||||
<div class="alert alert-success w-100 text-center mt-4" style="background: #d1fae5; color: #065f46; padding: 1rem; border-radius: 8px; border: 1px solid #a7f3d0;">
|
||||
Thank you for your feedback! We will get back to you shortly. Please use Ticket ID: <strong>{{ ticket_id }}</strong> for future reference.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Right: Contact Info -->
|
||||
<div class="contact-info-wrapper">
|
||||
<h1 class="contact-heading">Contact us</h1>
|
||||
<p class="contact-subheading">
|
||||
It is very important for us to keep in touch with you, so we are always ready to answer any questions that interest you. Shoot!
|
||||
</p>
|
||||
|
||||
<!-- Contact Details -->
|
||||
<div class="contact-details">
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<i class="fas fa-building"></i>
|
||||
</div>
|
||||
<div class="contact-content">
|
||||
<h4>HEAD OFFICE</h4>
|
||||
<p><strong>ADDRESS:</strong></p>
|
||||
<p>D - 878, LGF, New Friends Colony,</p>
|
||||
<p>New Delhi - 110025, India</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<i class="fas fa-phone"></i>
|
||||
</div>
|
||||
<div class="contact-content">
|
||||
<h4>PHONE NUMBERS</h4>
|
||||
<p><strong>Customer Care:</strong> +91 92204 33466</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="contact-item">
|
||||
<div class="contact-icon">
|
||||
<i class="fas fa-envelope"></i>
|
||||
</div>
|
||||
<div class="contact-content">
|
||||
<h4>EMAIL</h4>
|
||||
<p>info@registeryourstartup.com</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- Global Offices Section -->
|
||||
<section id="offices" class="py-16 bg-gray-50">
|
||||
<div class="container mx-auto px-4">
|
||||
<h2 class="section-title">Our Global Offices</h2>
|
||||
<div class="offices-grid">
|
||||
<!-- UAE Office -->
|
||||
<div class="office-card">
|
||||
<h3><i class="fas fa-building"></i> U.A.E Office</h3>
|
||||
<p><strong>Visalite Global FZCO</strong></p>
|
||||
<p>Scality Office No 63, Building No 9WC 523</p>
|
||||
<p>PO Box 491, Dubai Airport Freezone</p>
|
||||
<p>Dubai, UAE</p>
|
||||
</div>
|
||||
|
||||
<!-- USA Office -->
|
||||
<div class="office-card">
|
||||
<h3><i class="fas fa-flag-usa"></i> U.S. Office</h3>
|
||||
<p>3240 E-State Street, Hamilton</p>
|
||||
<p>New Jersey 08619</p>
|
||||
<p>United States</p>
|
||||
</div>
|
||||
|
||||
<!-- UK Office -->
|
||||
<div class="office-card">
|
||||
<h3><i class="fas fa-landmark"></i> U.K. Office</h3>
|
||||
<p>8 Alexandra Road, Worthing</p>
|
||||
<p>West Sussex BN 11 2DX</p>
|
||||
<p>United Kingdom</p>
|
||||
</div>
|
||||
|
||||
<!-- Saudi Arabia Office -->
|
||||
<div class="office-card">
|
||||
<h3><i class="fas fa-mosque"></i> Saudi Arabia Office</h3>
|
||||
<p>Building No. 4219, Al Izdihar Street</p>
|
||||
<p>Unit No. 4301, Riyadh 12486</p>
|
||||
<p>Saudi Arabia</p>
|
||||
</div>
|
||||
|
||||
<!-- Malaysia Office -->
|
||||
<div class="office-card">
|
||||
<h3><i class="fas fa-university"></i> Malaysia Office</h3>
|
||||
<p>Landmark, Suite 1705, Level 17</p>
|
||||
<p>12 Jalan Ngee Heng</p>
|
||||
<p>80000 Johor Bahru, Johor</p>
|
||||
<p>Malaysia</p>
|
||||
</div>
|
||||
|
||||
<!-- Singapore Office -->
|
||||
<div class="office-card">
|
||||
<h3><i class="fas fa-city"></i> Singapore Office</h3>
|
||||
<p>3 Shenton Way, #09-07</p>
|
||||
<p>Shenton House</p>
|
||||
<p>Singapore 068805</p>
|
||||
</div>
|
||||
|
||||
<!-- Indonesia Office -->
|
||||
<div class="office-card">
|
||||
<h3><i class="fas fa-torii-gate"></i> Indonesia Office</h3>
|
||||
<p>Cyber 2 Tower, Rasuna Said</p>
|
||||
<p>Kuningan, Jakarta</p>
|
||||
<p><strong>Phone:</strong> +62 812-863-18349</p>
|
||||
</div>
|
||||
|
||||
<!-- Kenya Office -->
|
||||
<div class="office-card">
|
||||
<h3><i class="fas fa-globe-africa"></i> Kenya Office</h3>
|
||||
<p>#7 Vakaria Investment</p>
|
||||
<p>Mombasa Road, Nairobi</p>
|
||||
<p>Kenya</p>
|
||||
</div>
|
||||
|
||||
<!-- Bangalore Office -->
|
||||
<div class="office-card">
|
||||
<h3><i class="fas fa-laptop-code"></i> Bangalore Office</h3>
|
||||
<p>Bizcon Services Toyama Bizhub</p>
|
||||
<p>Second Floor, Near Manyata Tech Park</p>
|
||||
<p>Thannisandra Main Road</p>
|
||||
<p>Bangalore 560077, India</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section class="py-16 bg-gradient-to-r from-indigo-500 to-purple-600">
|
||||
<div class="container mx-auto px-4 text-center">
|
||||
<h2 class="text-3xl md:text-4xl font-bold text-white mb-4">Ready to Start Your Business Journey?</h2>
|
||||
<p class="text-indigo-100 text-lg mb-8 max-w-2xl mx-auto">Contact us today and let our expert team guide you through seamless business setup and expansion</p>
|
||||
<div class="flex flex-col sm:flex-row justify-center space-y-4 sm:space-y-0 sm:space-x-4">
|
||||
<a href="#contact-form" class="bg-white text-indigo-600 px-8 py-3 rounded-full font-medium hover:bg-indigo-50 transition">Send Message</a>
|
||||
<a href="{% url 'book_appointment' %}" class="border border-white text-white px-8 py-3 rounded-full font-medium hover:bg-white hover:text-indigo-600 transition">Book Appointment</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
// Form validation
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const form = document.querySelector('form');
|
||||
const subjectInput = document.getElementById('subject');
|
||||
const messageInput = document.getElementById('message');
|
||||
const emailInput = document.getElementById('email');
|
||||
const phoneInput = document.getElementById('phone');
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
let isValid = true;
|
||||
|
||||
// Clear previous errors
|
||||
document.querySelectorAll('.error').forEach(error => error.textContent = '');
|
||||
|
||||
// Validate subject
|
||||
if (!subjectInput.value.trim()) {
|
||||
document.getElementById('subject-error').textContent = 'Subject is required';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Validate message
|
||||
if (!messageInput.value.trim()) {
|
||||
document.getElementById('message-error').textContent = 'Message is required';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Validate email if present
|
||||
if (emailInput && !emailInput.value.trim()) {
|
||||
document.getElementById('email-error').textContent = 'Email is required';
|
||||
isValid = false;
|
||||
} else if (emailInput && emailInput.value.trim()) {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(emailInput.value)) {
|
||||
document.getElementById('email-error').textContent = 'Please enter a valid email address';
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
3
at_django_boilerplate/communications/tests.py
Executable file
3
at_django_boilerplate/communications/tests.py
Executable file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
7
at_django_boilerplate/communications/urls.py
Executable file
7
at_django_boilerplate/communications/urls.py
Executable file
@@ -0,0 +1,7 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('contact/', views.ContactView.as_view(), name='contact_us_form'),
|
||||
path('book-appointment/', views.AppointmentView.as_view(), name='book_appointment'),
|
||||
]
|
||||
190
at_django_boilerplate/communications/views.py
Executable file
190
at_django_boilerplate/communications/views.py
Executable file
@@ -0,0 +1,190 @@
|
||||
from django.shortcuts import render, redirect
|
||||
from django.core.mail import send_mail
|
||||
from django.conf import settings
|
||||
from django.views.generic import View
|
||||
from django.contrib import messages
|
||||
from django.utils import timezone # Ensure this is imported
|
||||
|
||||
from at_django_boilerplate.accounts.models import CustomUser
|
||||
from at_django_boilerplate.communications.models import FeedbackModel, AppointmentModel
|
||||
from at_django_boilerplate.backend_admin.models import SEOConfiguration
|
||||
|
||||
|
||||
class ContactView(View):
|
||||
template_name = 'contact_us_form.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
seo = SEOConfiguration.objects.first()
|
||||
context = {
|
||||
'user': request.user,
|
||||
'meta_title': seo.contact_meta_title if seo else "Contact Us - RegisterYourStartup",
|
||||
'meta_description': seo.contact_meta_description if seo else "Get in touch with our team."
|
||||
}
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
custom_user = None
|
||||
if request.user.is_authenticated:
|
||||
try:
|
||||
custom_user = request.user
|
||||
except CustomUser.DoesNotExist:
|
||||
pass
|
||||
|
||||
subject = request.POST.get('subject')
|
||||
message = request.POST.get('message')
|
||||
name = request.POST.get('name')
|
||||
|
||||
email = ''
|
||||
phone = ''
|
||||
full_name = name or "Anonymous User"
|
||||
|
||||
if custom_user:
|
||||
email = custom_user.get_decrypted_email()
|
||||
phone = custom_user.get_decrypted_contact_number()
|
||||
full_name = custom_user.get_full_name() or name or "Authenticated User"
|
||||
else:
|
||||
email = request.POST.get('email')
|
||||
phone = request.POST.get('phone')
|
||||
|
||||
# Save feedback
|
||||
feedback = FeedbackModel(
|
||||
user=custom_user,
|
||||
name=full_name,
|
||||
email=email,
|
||||
phone=phone,
|
||||
subject=subject,
|
||||
message=message
|
||||
)
|
||||
feedback.save()
|
||||
ticket_id = feedback.id
|
||||
|
||||
# Prepare and send email
|
||||
email_message = f'''
|
||||
Ticket ID:\t{ticket_id}
|
||||
Name:\t{full_name}
|
||||
Email:\t{email}
|
||||
Contact Number:\t{phone or 'Not provided'}
|
||||
Subject:\t{subject}
|
||||
Message:\t{message}
|
||||
'''
|
||||
|
||||
try:
|
||||
send_mail(
|
||||
subject='You have a new message on RegisterYourStartup',
|
||||
message=email_message,
|
||||
from_email=settings.EMAIL_HOST_USER,
|
||||
recipient_list=[settings.EMAIL_HOST_USER],
|
||||
fail_silently=False,
|
||||
)
|
||||
except Exception as e:
|
||||
print("Email sending failed:", e)
|
||||
|
||||
messages.success(
|
||||
request,
|
||||
f"Thank you, <strong>{full_name.split()[0] if full_name.split() else 'there'}</strong>! "
|
||||
f"Your message has been sent successfully. "
|
||||
f"Your Ticket ID is <strong>{ticket_id}</strong>. We will get back to you shortly."
|
||||
)
|
||||
|
||||
return redirect('contact_us_form')
|
||||
|
||||
|
||||
class AppointmentView(View):
|
||||
template_name = 'book_appointment.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
seo = SEOConfiguration.objects.first()
|
||||
context = {
|
||||
'meta_title': seo.appointment_meta_title if seo and seo.appointment_meta_title else "Book an Appointment - RegisterYourStartup.com",
|
||||
'meta_description': seo.appointment_meta_description if seo and seo.appointment_meta_description else "Schedule a consultation with our business experts.",
|
||||
}
|
||||
return render(request, self.template_name, context)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
custom_user = None
|
||||
if request.user.is_authenticated:
|
||||
try:
|
||||
custom_user = request.user
|
||||
except CustomUser.DoesNotExist:
|
||||
pass
|
||||
|
||||
full_name = request.POST.get('fullName')
|
||||
email = request.POST.get('email')
|
||||
phone = request.POST.get('phone')
|
||||
meeting_with = request.POST.get('meetingWith')
|
||||
appointment_datetime_str = request.POST.get('appointmentDateTime')
|
||||
notes = request.POST.get('notes', '')
|
||||
|
||||
# Basic server-side validation
|
||||
if not all([full_name, email, phone, meeting_with, appointment_datetime_str]):
|
||||
messages.error(request, "All required fields must be filled.")
|
||||
return redirect('book_appointment')
|
||||
|
||||
try:
|
||||
# Parse the datetime-local input (format: YYYY-MM-DDTHH:MM)
|
||||
# Replace 'T' with space to make it compatible with fromisoformat
|
||||
naive_datetime = timezone.datetime.fromisoformat(
|
||||
appointment_datetime_str.replace('T', ' ')
|
||||
)
|
||||
|
||||
# Convert to timezone-aware datetime
|
||||
appointment_datetime = timezone.make_aware(naive_datetime)
|
||||
|
||||
if appointment_datetime <= timezone.now():
|
||||
messages.error(request, "Appointment time must be in the future.")
|
||||
return redirect('book_appointment')
|
||||
except ValueError:
|
||||
messages.error(request, "Invalid date/time format.")
|
||||
return redirect('book_appointment')
|
||||
|
||||
# Save appointment
|
||||
appointment = AppointmentModel(
|
||||
user=custom_user,
|
||||
full_name=full_name,
|
||||
email=email,
|
||||
phone=phone,
|
||||
meeting_with=meeting_with,
|
||||
appointment_datetime=appointment_datetime,
|
||||
notes=notes
|
||||
)
|
||||
appointment.save()
|
||||
|
||||
ticket_id = f"APP-{appointment.pk:06d}"
|
||||
|
||||
# Send email notification to admin
|
||||
email_subject = f"New Appointment Booking - {ticket_id}"
|
||||
email_message = f"""
|
||||
New Appointment Request
|
||||
|
||||
Ticket ID: {ticket_id}
|
||||
Name: {full_name}
|
||||
Email: {email}
|
||||
Phone: {phone}
|
||||
Meet With: {appointment.get_meeting_with_display()}
|
||||
Date & Time: {appointment_datetime.strftime('%d %B %Y, %I:%M %p')}
|
||||
Notes: {notes or 'No additional notes'}
|
||||
|
||||
Please confirm or follow up with the user.
|
||||
"""
|
||||
|
||||
try:
|
||||
send_mail(
|
||||
subject=email_subject,
|
||||
message=email_message,
|
||||
from_email=settings.EMAIL_HOST_USER,
|
||||
recipient_list=[settings.EMAIL_HOST_USER],
|
||||
fail_silently=False,
|
||||
)
|
||||
except Exception as e:
|
||||
print("Appointment email failed:", e)
|
||||
|
||||
# Success message via Django messages (shown on redirect)
|
||||
messages.success(
|
||||
request,
|
||||
f"Thank you, <strong>{full_name.split()[0] if full_name.split() else 'there'}</strong>! "
|
||||
f"Your appointment has been booked successfully. "
|
||||
f"Your Ticket ID is <strong>{ticket_id}</strong>. We will contact you shortly to confirm."
|
||||
)
|
||||
|
||||
# Redirect to same page to show success message and prevent resubmission
|
||||
return redirect('book_appointment')
|
||||
Reference in New Issue
Block a user