generic contact form
This commit is contained in:
parent
eece05af74
commit
2cbcc4ba98
9 changed files with 280 additions and 48 deletions
|
@ -0,0 +1,39 @@
|
||||||
|
# Generated by Django 5.1.5 on 2025-02-27 14:33
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("services", "0007_service_is_coming_soon"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="lead",
|
||||||
|
name="company",
|
||||||
|
field=models.CharField(blank=True, max_length=200, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="lead",
|
||||||
|
name="message",
|
||||||
|
field=models.TextField(blank=True, max_length=1000, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="lead",
|
||||||
|
name="phone",
|
||||||
|
field=models.CharField(blank=True, max_length=50, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="lead",
|
||||||
|
name="service",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
on_delete=django.db.models.deletion.SET_NULL,
|
||||||
|
to="services.service",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -276,18 +276,22 @@ class ExternalLink(models.Model):
|
||||||
|
|
||||||
|
|
||||||
class Lead(models.Model):
|
class Lead(models.Model):
|
||||||
service = models.ForeignKey(Service, on_delete=models.CASCADE)
|
name = models.CharField(max_length=200)
|
||||||
|
email = models.EmailField()
|
||||||
|
company = models.CharField(max_length=200, null=True, blank=True)
|
||||||
|
phone = models.CharField(max_length=50, null=True, blank=True)
|
||||||
|
message = models.TextField(blank=True, null=True, max_length=1000)
|
||||||
|
odoo_lead_id = models.IntegerField(null=True, blank=True)
|
||||||
|
|
||||||
|
service = models.ForeignKey(
|
||||||
|
Service, on_delete=models.SET_NULL, null=True, blank=True
|
||||||
|
)
|
||||||
offering = models.ForeignKey(
|
offering = models.ForeignKey(
|
||||||
ServiceOffering, on_delete=models.SET_NULL, null=True, blank=True
|
ServiceOffering, on_delete=models.SET_NULL, null=True, blank=True
|
||||||
)
|
)
|
||||||
plan = models.ForeignKey(Plan, on_delete=models.SET_NULL, null=True, blank=True)
|
plan = models.ForeignKey(Plan, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
name = models.CharField(max_length=200)
|
|
||||||
company = models.CharField(max_length=200)
|
|
||||||
email = models.EmailField()
|
|
||||||
phone = models.CharField(max_length=50)
|
|
||||||
message = models.TextField(blank=True, max_length=1000)
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
odoo_lead_id = models.IntegerField(null=True, blank=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.name} - {self.company} ({self.service})"
|
return f"{self.name} - {self.company} ({self.service})"
|
||||||
|
|
|
@ -56,36 +56,48 @@ class OdooAPI:
|
||||||
def create_lead(self, lead, source="form"):
|
def create_lead(self, lead, source="form"):
|
||||||
"""Create a lead in Odoo"""
|
"""Create a lead in Odoo"""
|
||||||
try:
|
try:
|
||||||
logger.info(
|
logger.info(f"Attempting to create lead for {lead.name}")
|
||||||
f"Attempting to create lead for {lead.name} from {lead.company}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get provider name from offering if it exists
|
|
||||||
provider_name = ""
|
|
||||||
if hasattr(lead, "offering") and lead.offering:
|
|
||||||
provider_name = lead.offering.cloud_provider.name
|
|
||||||
|
|
||||||
# Prepare service description
|
# Prepare service description
|
||||||
service_details = []
|
service_details = []
|
||||||
if lead.service:
|
|
||||||
|
if hasattr(lead, "service") and lead.service:
|
||||||
service_details.append(f"Service: {lead.service.name}")
|
service_details.append(f"Service: {lead.service.name}")
|
||||||
if provider_name:
|
|
||||||
service_details.append(f"Provider: {provider_name}")
|
# Get provider name from offering if it exists
|
||||||
if lead.plan:
|
if (
|
||||||
service_details.append(f"Plan: {lead.plan.name}")
|
hasattr(lead, "offering")
|
||||||
|
and lead.offering
|
||||||
|
and hasattr(lead.offering, "cloud_provider")
|
||||||
|
):
|
||||||
|
provider_name = lead.offering.cloud_provider.name
|
||||||
|
service_details.append(f"Provider: {provider_name}")
|
||||||
|
|
||||||
|
if hasattr(lead, "plan") and lead.plan:
|
||||||
|
service_details.append(f"Plan: {lead.plan.name}")
|
||||||
|
|
||||||
if source == "form":
|
if source == "form":
|
||||||
service_details.append(f"Source: Servala Website")
|
service_details.append(f"Source: Servala Website")
|
||||||
elif source == "osbapi":
|
elif source == "osbapi":
|
||||||
service_details.append(f"Source: OSB API")
|
service_details.append(f"Source: OSB API")
|
||||||
|
elif source == "contact_form":
|
||||||
|
service_details.append(f"Source: Contact Form")
|
||||||
|
|
||||||
|
# Prepare lead name based on whether it's a service lead or generic contact
|
||||||
|
if hasattr(lead, "service") and lead.service:
|
||||||
|
lead_name = f"Servala - Interest in {lead.service.name}"
|
||||||
|
else:
|
||||||
|
lead_name = "Servala - Website Contact"
|
||||||
|
|
||||||
# Prepare lead data
|
# Prepare lead data
|
||||||
lead_data = {
|
lead_data = {
|
||||||
"name": f"Servala - Interest in {lead.service.name}",
|
"name": lead_name,
|
||||||
"contact_name": lead.name,
|
"contact_name": lead.name,
|
||||||
"partner_name": lead.company,
|
"partner_name": (
|
||||||
|
lead.company if hasattr(lead, "company") and lead.company else ""
|
||||||
|
),
|
||||||
"email_from": lead.email,
|
"email_from": lead.email,
|
||||||
"phone": lead.phone,
|
"phone": lead.phone if hasattr(lead, "phone") and lead.phone else "",
|
||||||
"description": (
|
"description": (
|
||||||
f"{lead.message}<br/><br/>" + "<br/>".join(service_details)
|
f"{lead.message}<br/><br/>" + "<br/>".join(service_details)
|
||||||
if lead.message
|
if lead.message
|
||||||
|
@ -111,29 +123,50 @@ class OdooAPI:
|
||||||
logger.info(f"Successfully created lead in Odoo with ID: {odoo_lead_id}")
|
logger.info(f"Successfully created lead in Odoo with ID: {odoo_lead_id}")
|
||||||
|
|
||||||
# Post message in chatter
|
# Post message in chatter
|
||||||
message_data = {
|
if hasattr(lead, "service") and lead.service:
|
||||||
"body": f"""
|
# For service-related leads
|
||||||
<p>Thank you for your interest in the service <strong>{lead.service.name}</strong>.
|
message_data = {
|
||||||
We recorded the following information and will get back to you soon.</p><br/>
|
"body": f"""
|
||||||
|
<p>Thank you for your interest in the service <strong>{lead.service.name}</strong>.
|
||||||
|
We recorded the following information and will get back to you soon:</p><br/>
|
||||||
|
|
||||||
<p>Contact Details:</p>
|
<p>Contact Details:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Name:</strong> {lead.name}</li>
|
<li><strong>Name:</strong> {lead.name}</li>
|
||||||
<li><strong>Company:</strong> {lead.company}</li>
|
<li><strong>Company:</strong> {lead.company if hasattr(lead, "company") and lead.company else "N/A"}</li>
|
||||||
<li><strong>Email:</strong> {lead.email}</li>
|
<li><strong>Email:</strong> {lead.email}</li>
|
||||||
<li><strong>Phone:</strong> {lead.phone}</li>
|
<li><strong>Phone:</strong> {lead.phone if hasattr(lead, "phone") and lead.phone else "N/A"}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>Service Details:<p>
|
<p>Service Details:<p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Service:</strong> {lead.service.name}</li>
|
<li><strong>Service:</strong> {lead.service.name}</li>
|
||||||
{f'<li><strong>Provider:</strong> {provider_name}</li>' if provider_name else ''}
|
{f'<li><strong>Provider:</strong> {provider_name}</li>' if provider_name else ''}
|
||||||
{f'<li><strong>Plan:</strong> {lead.plan.name}</li>' if lead.plan else ''}
|
{f'<li><strong>Plan:</strong> {lead.plan.name}</li>' if lead.plan else ''}
|
||||||
</ul>
|
</ul>
|
||||||
""",
|
""",
|
||||||
"message_type": "comment",
|
"message_type": "comment",
|
||||||
"subtype_xmlid": "mail.mt_comment",
|
"subtype_xmlid": "mail.mt_comment",
|
||||||
}
|
}
|
||||||
|
else:
|
||||||
|
# For contact form submissions
|
||||||
|
message_data = {
|
||||||
|
"body": f"""
|
||||||
|
<p>Thank you for contacting Servala by VSHN.<br/>
|
||||||
|
We recorded the following information and will get back to you soon:</p><br/>
|
||||||
|
|
||||||
|
<p>Contact Details:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Name:</strong> {lead.name}</li>
|
||||||
|
<li><strong>Email:</strong> {lead.email}</li>
|
||||||
|
{f'<li><strong>Phone:</strong> {lead.phone}</li>' if lead.phone else ''}
|
||||||
|
{f'<li><strong>Company:</strong> {lead.company}</li>' if lead.company else ''}
|
||||||
|
{f'<li><strong>Message:</strong> {lead.message}</li>' if lead.message else ''}
|
||||||
|
</ul>
|
||||||
|
""",
|
||||||
|
"message_type": "comment",
|
||||||
|
"subtype_xmlid": "mail.mt_comment",
|
||||||
|
}
|
||||||
|
|
||||||
# Post the message
|
# Post the message
|
||||||
self.odoo.env["crm.lead"].browse(odoo_lead_id).message_post(
|
self.odoo.env["crm.lead"].browse(odoo_lead_id).message_post(
|
||||||
|
|
|
@ -37,6 +37,9 @@
|
||||||
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:provider_list' %}">Cloud Providers</a></li>
|
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:provider_list' %}">Cloud Providers</a></li>
|
||||||
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:partner_list' %}">Consulting Partners</a></li>
|
<li class="menu__item"><a class="menu__item-link" href="{% url 'services:partner_list' %}">Consulting Partners</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<ul class="menu-cta mb-0">
|
||||||
|
<li class="mr-17"><a class="btn btn-outline-light btn-outline-primary" href="{% url 'services:contact' %}" role="button">Contact</a></li>
|
||||||
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav__toggle">
|
<div class="nav__toggle">
|
||||||
|
|
82
hub/services/templates/services/contact_form.html
Normal file
82
hub/services/templates/services/contact_form.html
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
{% extends 'services/base.html' %}
|
||||||
|
{% load form_tags %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body p-4">
|
||||||
|
<h2 class="mb-4">Contact Us</h2>
|
||||||
|
|
||||||
|
{% if messages %}
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="alert alert-{{ message.tags }}">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.name.id_for_label }}" class="form-label">Your Name</label>
|
||||||
|
{{ form.name }}
|
||||||
|
{% if form.name.errors %}
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{ form.name.errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.email.id_for_label }}" class="form-label">Your Email Address</label>
|
||||||
|
{{ form.email }}
|
||||||
|
{% if form.email.errors %}
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{ form.email.errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.phone.id_for_label }}" class="form-label">Your Phone Number (Optional)</label>
|
||||||
|
{{ form.phone }}
|
||||||
|
{% if form.phone.errors %}
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{ form.phone.errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.company.id_for_label }}" class="form-label">Your Company (Optional)</label>
|
||||||
|
{{ form.company }}
|
||||||
|
{% if form.company.errors %}
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{ form.company.errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="{{ form.message.id_for_label }}" class="form-label">Your Message (Optional)</label>
|
||||||
|
{{ form.message }}
|
||||||
|
{% if form.message.errors %}
|
||||||
|
<div class="invalid-feedback d-block">
|
||||||
|
{{ form.message.errors }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-4">
|
||||||
|
<button type="submit" class="btn btn-primary btn-lg">Send Message</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
18
hub/services/templates/services/contact_thank_you.html
Normal file
18
hub/services/templates/services/contact_thank_you.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends 'services/base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container py-5">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8 text-center">
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-body p-5">
|
||||||
|
<h2 class="mb-4">Thank You!</h2>
|
||||||
|
<p class="lead">Your message has been sent successfully.</p>
|
||||||
|
<p>We'll get back to you as soon as possible.</p>
|
||||||
|
<a href="{% url 'services:homepage' %}" class="btn btn-primary mt-3">Back to Home</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -35,7 +35,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-40">
|
<div class="mb-40">
|
||||||
<label for="{{ form.phone.id_for_label }}" class="form-label text-purple">Your Phone Number</label>
|
<label for="{{ form.phone.id_for_label }}" class="form-label text-purple">Your Phone Number (Optional)</label>
|
||||||
{{ form.phone|addclass:"form-control" }}
|
{{ form.phone|addclass:"form-control" }}
|
||||||
{% if form.phone.errors %}
|
{% if form.phone.errors %}
|
||||||
<div class="invalid-feedback d-block">
|
<div class="invalid-feedback d-block">
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-40">
|
<div class="mb-40">
|
||||||
<label for="{{ form.company.id_for_label }}" class="form-label text-purple">Your Company</label>
|
<label for="{{ form.company.id_for_label }}" class="form-label text-purple">Your Company (Optional)</label>
|
||||||
{{ form.company|addclass:"form-control" }}
|
{{ form.company|addclass:"form-control" }}
|
||||||
{% if form.company.errors %}
|
{% if form.company.errors %}
|
||||||
<div class="invalid-feedback d-block">
|
<div class="invalid-feedback d-block">
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-40">
|
<div class="mb-40">
|
||||||
<label for="{{ form.message.id_for_label }}" class="form-label text-purple">Message (Optional)</label>
|
<label for="{{ form.message.id_for_label }}" class="form-label text-purple">Your Message (Optional)</label>
|
||||||
{{ form.message|addclass:"form-control" }}
|
{{ form.message|addclass:"form-control" }}
|
||||||
{% if form.message.errors %}
|
{% if form.message.errors %}
|
||||||
<div class="invalid-feedback d-block">
|
<div class="invalid-feedback d-block">
|
||||||
|
|
|
@ -15,4 +15,6 @@ urlpatterns = [
|
||||||
path("partner/<slug:slug>/", views.partner_detail, name="partner_detail"),
|
path("partner/<slug:slug>/", views.partner_detail, name="partner_detail"),
|
||||||
path("service/<slug:slug>/interest/", views.create_lead, name="create_lead"),
|
path("service/<slug:slug>/interest/", views.create_lead, name="create_lead"),
|
||||||
path("service/<slug:slug>/thank-you/", views.thank_you, name="thank_you"),
|
path("service/<slug:slug>/thank-you/", views.thank_you, name="thank_you"),
|
||||||
|
path("contact/", views.leads.contact, name="contact"),
|
||||||
|
path("contact/thank-you/", views.leads.contact_thank_you, name="contact_thank_you"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -91,3 +91,54 @@ def create_lead(request, slug):
|
||||||
def thank_you(request, slug):
|
def thank_you(request, slug):
|
||||||
service = get_object_or_404(Service, slug=slug)
|
service = get_object_or_404(Service, slug=slug)
|
||||||
return render(request, "services/thank_you.html", {"service": service})
|
return render(request, "services/thank_you.html", {"service": service})
|
||||||
|
|
||||||
|
|
||||||
|
def contact(request):
|
||||||
|
if request.method == "POST":
|
||||||
|
form = LeadForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
# Create a minimal Lead object
|
||||||
|
from hub.services.models import Lead
|
||||||
|
|
||||||
|
lead = Lead(
|
||||||
|
name=form.cleaned_data["name"],
|
||||||
|
email=form.cleaned_data["email"],
|
||||||
|
message=form.cleaned_data["message"],
|
||||||
|
company=form.cleaned_data["company"],
|
||||||
|
phone=form.cleaned_data["phone"],
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
logger.info(f"Attempting to create contact lead from {lead.name}")
|
||||||
|
odoo = OdooAPI()
|
||||||
|
odoo_lead_id = odoo.create_lead(lead, source="contact_form")
|
||||||
|
lead.odoo_lead_id = odoo_lead_id
|
||||||
|
lead.save()
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Successfully created contact lead with Odoo ID: {odoo_lead_id}"
|
||||||
|
)
|
||||||
|
messages.success(
|
||||||
|
request, "Thank you for your message. We'll get back to you soon!"
|
||||||
|
)
|
||||||
|
return redirect("services:contact_thank_you")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Failed to create contact lead: {str(e)}", exc_info=True)
|
||||||
|
error_message = "Sorry, there was an error processing your request. Please try again later."
|
||||||
|
|
||||||
|
if settings.DEBUG:
|
||||||
|
error_message += f" Error: {str(e)}"
|
||||||
|
|
||||||
|
messages.error(request, error_message)
|
||||||
|
else:
|
||||||
|
form = LeadForm()
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"form": form,
|
||||||
|
}
|
||||||
|
return render(request, "services/contact_form.html", context)
|
||||||
|
|
||||||
|
|
||||||
|
def contact_thank_you(request):
|
||||||
|
return render(request, "services/contact_thank_you.html")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue