Compare commits
2
Commits
bd6aa4b3f3
...
a817b9caf7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a817b9caf7 | ||
|
|
46abaa6917 |
+3
-2
@@ -15,7 +15,8 @@ SITE_TAGLINE=MKDRealtor.com · EXIT Realty
|
||||
PUBLIC_SITE_URL=http://127.0.0.1:8000
|
||||
CONTACT_PHONE=(630) 452-4443
|
||||
CONTACT_EMAIL=moni.dhill@gmail.com
|
||||
CONTACT_SERVICE_AREA=Serving Chicagoland
|
||||
CONTACT_ADDRESS=1245 Butterfield Rd. #100, Wheaton, IL 60189
|
||||
CONTACT_SERVICE_AREA=Serving Chicago's western suburbs
|
||||
CREDIT_NAME=AI ML Operations, LLC
|
||||
CREDIT_URL=https://aimloperations.com
|
||||
|
||||
@@ -59,7 +60,7 @@ PCM_WEBHOOK_SECRETS=
|
||||
# PCM_WEBHOOK_SECRET=
|
||||
# Required return address on every postcard order.
|
||||
# Quote the JSON (compose/shell break on bare {…, …}).
|
||||
# PCM_RETURN_ADDRESS='{"firstName":"Monica","lastName":"Dhillon","address":"123 Main St","city":"Naperville","state":"IL","zipCode":"60540"}'
|
||||
# PCM_RETURN_ADDRESS='{"firstName":"Monica","lastName":"Dhillon","address":"1245 Butterfield Rd.","address2":"#100","city":"Wheaton","state":"IL","zipCode":"60189"}'
|
||||
PCM_RETURN_ADDRESS=
|
||||
# Or set fields individually if JSON is empty:
|
||||
# PCM_RETURN_LINE1=
|
||||
|
||||
+10
-1
@@ -1,6 +1,15 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from analytics.models import Attribution, UTMVisit
|
||||
from analytics.models import Attribution, PageView, UTMVisit
|
||||
|
||||
|
||||
@admin.register(PageView)
|
||||
class PageViewAdmin(admin.ModelAdmin):
|
||||
list_display = ("path", "created_at")
|
||||
list_filter = ("created_at",)
|
||||
search_fields = ("path",)
|
||||
date_hierarchy = "created_at"
|
||||
readonly_fields = ("id", "path", "created_at", "updated_at")
|
||||
|
||||
|
||||
@admin.register(UTMVisit)
|
||||
|
||||
@@ -1,10 +1,54 @@
|
||||
import logging
|
||||
|
||||
from django.utils.crypto import get_random_string
|
||||
|
||||
from analytics.models import Attribution, UTMVisit
|
||||
from analytics.models import Attribution, PageView, UTMVisit
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CORRELATION_COOKIE = "ms_cid"
|
||||
|
||||
# Portal, auth, health, assets, and transactional public endpoints.
|
||||
_SKIP_PREFIXES = (
|
||||
"/portal/",
|
||||
"/admin/",
|
||||
"/accounts/",
|
||||
"/api/",
|
||||
"/healthz",
|
||||
"/static/",
|
||||
"/media/",
|
||||
"/unsubscribe/",
|
||||
)
|
||||
_SKIP_PATHS = frozenset({"/robots.txt", "/sitemap.xml", "/favicon.ico"})
|
||||
|
||||
|
||||
class PublicPageViewMiddleware:
|
||||
"""Record successful GET hits on public marketing pages."""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
if _should_record_page_view(request, response):
|
||||
try:
|
||||
PageView.objects.create(path=request.path[:512])
|
||||
except Exception:
|
||||
logger.exception("Failed to record page view for %s", request.path)
|
||||
return response
|
||||
|
||||
|
||||
def _should_record_page_view(request, response) -> bool:
|
||||
if request.method != "GET":
|
||||
return False
|
||||
if getattr(response, "status_code", 0) != 200:
|
||||
return False
|
||||
path = request.path
|
||||
if path in _SKIP_PATHS:
|
||||
return False
|
||||
return not any(path.startswith(prefix) for prefix in _SKIP_PREFIXES)
|
||||
|
||||
|
||||
class UTMTrackingMiddleware:
|
||||
"""Capture UTM params into UTMVisit and stash a correlation id cookie."""
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Generated by Django 6.1 on 2026-08-13 12:38
|
||||
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('analytics', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PageView',
|
||||
fields=[
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('path', models.CharField(db_index=True, max_length=512)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-created_at'],
|
||||
'indexes': [models.Index(fields=['created_at', 'path'], name='analytics_p_created_9e8b64_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -4,6 +4,21 @@ from core.models import TimeStampedModel, UUIDPrimaryKeyModel
|
||||
from leads.models import Lead
|
||||
|
||||
|
||||
class PageView(UUIDPrimaryKeyModel, TimeStampedModel):
|
||||
"""One successful GET of a public marketing page."""
|
||||
|
||||
path = models.CharField(max_length=512, db_index=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"]
|
||||
indexes = [
|
||||
models.Index(fields=["created_at", "path"]),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.path or "/"
|
||||
|
||||
|
||||
class UTMVisit(UUIDPrimaryKeyModel, TimeStampedModel):
|
||||
correlation_id = models.CharField(max_length=64, db_index=True)
|
||||
path = models.CharField(max_length=512, blank=True)
|
||||
|
||||
@@ -2,11 +2,37 @@
|
||||
{% block title %}Analytics · Portal{% endblock %}
|
||||
{% block topbar_title %}Analytics{% endblock %}
|
||||
{% block portal_content %}
|
||||
<div class="stat-row">
|
||||
<div class="analytics-overview">
|
||||
<div class="stat-card">
|
||||
<div class="label">Views (last 30 days)</div>
|
||||
<div class="value">{{ visits_last_30_days }}</div>
|
||||
<div class="value">{{ pageviews_last_30_days }}</div>
|
||||
</div>
|
||||
<div class="panel">
|
||||
<div class="panel-h"><h2>Top pages</h2><span class="muted" style="font-size:12px">Public visits, last 30 days</span></div>
|
||||
<div class="panel-b" style="padding:0">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Page</th>
|
||||
<th>Visits</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in top_pages %}
|
||||
<tr>
|
||||
<td>{{ row.path }}</td>
|
||||
<td>{{ row.count }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan="2" class="empty-state">No public page views in the last 30 days.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-row">
|
||||
<div class="stat-card">
|
||||
<div class="label">UTM landings</div>
|
||||
<div class="value">{{ total_visits }}</div>
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
|
||||
from analytics.models import PageView, UTMVisit
|
||||
|
||||
|
||||
class PublicPageViewTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
def test_home_records_page_view(self):
|
||||
response = self.client.get("/")
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(PageView.objects.count(), 1)
|
||||
self.assertEqual(PageView.objects.get().path, "/")
|
||||
|
||||
def test_about_records_page_view(self):
|
||||
self.client.get(reverse("public:about"))
|
||||
self.assertEqual(PageView.objects.filter(path="/about/").count(), 1)
|
||||
|
||||
def test_plain_visit_does_not_create_utm_visit(self):
|
||||
self.client.get("/")
|
||||
self.assertEqual(PageView.objects.count(), 1)
|
||||
self.assertEqual(UTMVisit.objects.count(), 0)
|
||||
|
||||
def test_utm_hit_records_both(self):
|
||||
self.client.get("/?utm_source=test&utm_campaign=demo")
|
||||
self.assertEqual(PageView.objects.count(), 1)
|
||||
self.assertEqual(UTMVisit.objects.count(), 1)
|
||||
|
||||
def test_healthz_not_recorded(self):
|
||||
self.client.get("/healthz/")
|
||||
self.assertEqual(PageView.objects.count(), 0)
|
||||
|
||||
def test_portal_and_admin_not_recorded(self):
|
||||
self.client.get("/portal/")
|
||||
self.client.get("/admin/")
|
||||
self.assertEqual(PageView.objects.count(), 0)
|
||||
|
||||
def test_robots_and_sitemap_not_recorded(self):
|
||||
self.client.get("/robots.txt")
|
||||
self.client.get("/sitemap.xml")
|
||||
self.assertEqual(PageView.objects.count(), 0)
|
||||
|
||||
def test_missing_page_not_recorded(self):
|
||||
response = self.client.get("/not-a-real-page/")
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(PageView.objects.count(), 0)
|
||||
|
||||
def test_contact_post_not_recorded(self):
|
||||
self.client.post(reverse("public:contact"), {})
|
||||
self.assertEqual(PageView.objects.count(), 0)
|
||||
|
||||
|
||||
class AnalyticsReportTests(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
user = get_user_model().objects.create_user(
|
||||
username="monica", password="pass-word-1"
|
||||
)
|
||||
self.client.force_login(user)
|
||||
|
||||
def test_views_card_uses_public_pageviews(self):
|
||||
self.client.get("/")
|
||||
self.client.get(reverse("public:about"))
|
||||
self.client.get(reverse("public:about"))
|
||||
stale = PageView.objects.create(path="/terms/")
|
||||
PageView.objects.filter(pk=stale.pk).update(
|
||||
created_at=timezone.now() - timedelta(days=31)
|
||||
)
|
||||
|
||||
response = self.client.get(reverse("analytics:report"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response.context["pageviews_last_30_days"], 3)
|
||||
top = {row["path"]: row["count"] for row in response.context["top_pages"]}
|
||||
self.assertEqual(top["/about/"], 2)
|
||||
self.assertEqual(top["/"], 1)
|
||||
self.assertNotIn("/terms/", top)
|
||||
self.assertContains(response, "Top pages")
|
||||
self.assertContains(response, "/about/")
|
||||
@@ -5,7 +5,7 @@ from django.db.models import Count
|
||||
from django.shortcuts import render
|
||||
from django.utils import timezone
|
||||
|
||||
from analytics.models import Attribution, UTMVisit
|
||||
from analytics.models import Attribution, PageView, UTMVisit
|
||||
|
||||
|
||||
def _bar_pct(rows, key="count"):
|
||||
@@ -18,7 +18,11 @@ def _bar_pct(rows, key="count"):
|
||||
@login_required
|
||||
def report(request):
|
||||
since_30d = timezone.now() - timedelta(days=30)
|
||||
visits_last_30_days = UTMVisit.objects.filter(created_at__gte=since_30d).count()
|
||||
pageviews_qs = PageView.objects.filter(created_at__gte=since_30d)
|
||||
pageviews_last_30_days = pageviews_qs.count()
|
||||
top_pages = list(
|
||||
pageviews_qs.values("path").annotate(count=Count("id")).order_by("-count")[:20]
|
||||
)
|
||||
|
||||
visits_by_source = _bar_pct(
|
||||
list(
|
||||
@@ -60,7 +64,8 @@ def report(request):
|
||||
request,
|
||||
"analytics/report.html",
|
||||
{
|
||||
"visits_last_30_days": visits_last_30_days,
|
||||
"pageviews_last_30_days": pageviews_last_30_days,
|
||||
"top_pages": top_pages,
|
||||
"total_visits": UTMVisit.objects.count(),
|
||||
"total_attributed": Attribution.objects.count(),
|
||||
"top_source": (top_visit or {}).get("utm_source") or "(direct)",
|
||||
|
||||
@@ -134,6 +134,7 @@ MIDDLEWARE = [
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"analytics.middleware.UTMTrackingMiddleware",
|
||||
"analytics.middleware.PublicPageViewMiddleware",
|
||||
"public.middleware.UnderConstructionMiddleware",
|
||||
]
|
||||
|
||||
@@ -215,7 +216,12 @@ SITE_TAGLINE = env("SITE_TAGLINE", "MKDRealtor.com · EXIT Realty Redefined")
|
||||
PUBLIC_SITE_URL = env("PUBLIC_SITE_URL", "")
|
||||
CONTACT_PHONE = env("CONTACT_PHONE", "(555) 123-4567")
|
||||
CONTACT_EMAIL = env("CONTACT_EMAIL", "monica@example.com")
|
||||
CONTACT_SERVICE_AREA = env("CONTACT_SERVICE_AREA", "Serving Greater Metro Area")
|
||||
CONTACT_ADDRESS = env(
|
||||
"CONTACT_ADDRESS", "1245 Butterfield Rd. #100, Wheaton, IL 60189"
|
||||
)
|
||||
CONTACT_SERVICE_AREA = env(
|
||||
"CONTACT_SERVICE_AREA", "Serving Chicago's western suburbs"
|
||||
)
|
||||
CREDIT_NAME = env("CREDIT_NAME", "AI ML Operations, LLC")
|
||||
CREDIT_URL = env("CREDIT_URL", "https://aimloperations.com")
|
||||
|
||||
|
||||
@@ -83,6 +83,22 @@ body.portal {
|
||||
|
||||
/* Portal widgets */
|
||||
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 16px; margin-bottom: 24px; }
|
||||
.analytics-overview {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(140px, 1fr) 4fr;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
align-items: stretch;
|
||||
}
|
||||
.analytics-overview .stat-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.analytics-overview .panel { margin-bottom: 0; }
|
||||
@media (max-width: 800px) {
|
||||
.analytics-overview { grid-template-columns: 1fr; }
|
||||
}
|
||||
.stat-card {
|
||||
background: #fff;
|
||||
border: 1px solid var(--monica-border);
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 123 KiB |
@@ -65,7 +65,7 @@
|
||||
{% endif %}
|
||||
<article class="unit align-items-center">
|
||||
<div class="unit-left"><span class="icon icon-md icon-modern mdi mdi-map-marker"></span></div>
|
||||
<div class="unit-body"><span class="link-default">{{ CONTACT_SERVICE_AREA }}</span></div>
|
||||
<div class="unit-body"><span class="link-default">{% if CONTACT_ADDRESS %}{{ CONTACT_ADDRESS }}{% else %}{{ CONTACT_SERVICE_AREA }}{% endif %}</span></div>
|
||||
</article>
|
||||
<a class="button button-gray-bordered button-winona" href="{% url 'public:contact' %}" data-tianji-event="nav_get_in_touch">Get in touch</a>
|
||||
</div>
|
||||
@@ -117,7 +117,7 @@
|
||||
<span style="color:#7dd3da;font-size:12px">{{ SITE_TAGLINE }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="footer-advanced-text">Local expertise, clear communication, and a marketing platform built to bring serious buyers and sellers to you — not to a generic campaign factory.</p>
|
||||
<p class="footer-advanced-text">Local expertise, clear communication, and a marketing platform built to bring serious buyers and sellers to you — not to a generic campaign factory.{% if CONTACT_ADDRESS %} {{ CONTACT_ADDRESS }}.{% endif %}</p>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<h5 class="font-weight-bold text-uppercase text-white">Navigate</h5>
|
||||
|
||||
@@ -20,8 +20,11 @@ def site_branding(request):
|
||||
"CONTACT_PHONE": phone,
|
||||
"CONTACT_PHONE_TEL": _phone_tel(phone),
|
||||
"CONTACT_EMAIL": settings.CONTACT_EMAIL or "",
|
||||
"CONTACT_ADDRESS": getattr(settings, "CONTACT_ADDRESS", "") or "",
|
||||
"CONTACT_SERVICE_AREA": getattr(
|
||||
settings, "CONTACT_SERVICE_AREA", "Serving Greater Metro Area"
|
||||
settings,
|
||||
"CONTACT_SERVICE_AREA",
|
||||
"Serving Chicago's western suburbs",
|
||||
),
|
||||
"CREDIT_NAME": getattr(settings, "CREDIT_NAME", "AI ML Operations, LLC"),
|
||||
"CREDIT_URL": getattr(
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
{% block title %}About {{ SITE_NAME }} · EXIT Realty Realtor{% endblock %}
|
||||
{% block og_title %}About {{ SITE_NAME }} · EXIT Realty Realtor{% endblock %}
|
||||
{% block twitter_title %}About {{ SITE_NAME }} · EXIT Realty Realtor{% endblock %}
|
||||
{% block meta_description %}Meet {{ SITE_NAME }}, a local EXIT Realty realtor helping families buy and sell homes with clear communication and marketing that reaches people. {{ CONTACT_SERVICE_AREA }}.{% endblock %}
|
||||
{% block og_description %}Meet {{ SITE_NAME }}, a local EXIT Realty realtor helping families buy and sell homes with clear communication and marketing that reaches people. {{ CONTACT_SERVICE_AREA }}.{% endblock %}
|
||||
{% block twitter_description %}Meet {{ SITE_NAME }}, a local EXIT Realty realtor helping families buy and sell homes with clear communication and marketing that reaches people. {{ CONTACT_SERVICE_AREA }}.{% endblock %}
|
||||
{% block meta_description %}Meet {{ SITE_NAME }}, your real estate partner in Chicago's western suburbs with EXIT Realty Redefined — buyers, sellers, and first-time homebuyers welcome. {{ CONTACT_SERVICE_AREA }}.{% endblock %}
|
||||
{% block og_description %}Meet {{ SITE_NAME }}, your real estate partner in Chicago's western suburbs with EXIT Realty Redefined — buyers, sellers, and first-time homebuyers welcome. {{ CONTACT_SERVICE_AREA }}.{% endblock %}
|
||||
{% block twitter_description %}Meet {{ SITE_NAME }}, your real estate partner in Chicago's western suburbs with EXIT Realty Redefined — buyers, sellers, and first-time homebuyers welcome. {{ CONTACT_SERVICE_AREA }}.{% endblock %}
|
||||
{% block og_image %}
|
||||
<meta property="og:image" content="{{ PUBLIC_SITE_URL }}{% static 'images/careers-1-570x388.jpg' %}">
|
||||
<meta property="og:image:alt" content="{{ SITE_NAME }}">
|
||||
<meta property="og:image" content="{{ PUBLIC_SITE_URL }}{% static 'images/monica-dhillon-headshot.jpg' %}">
|
||||
<meta property="og:image:alt" content="Professional headshot of {{ SITE_NAME }}, Real Estate Agent">
|
||||
{% endblock %}
|
||||
{% block structured_data %}
|
||||
<script type="application/ld+json">
|
||||
@@ -17,13 +17,15 @@
|
||||
"@type": "AboutPage",
|
||||
"name": "About {{ SITE_NAME|escapejs }}",
|
||||
"url": "{{ PUBLIC_SITE_URL }}{% url 'public:about' %}",
|
||||
"description": "Meet {{ SITE_NAME|escapejs }}, a local EXIT Realty realtor helping families buy and sell homes with clear communication and marketing that reaches people.",
|
||||
"description": "Meet {{ SITE_NAME|escapejs }}, your real estate partner in Chicago's western suburbs with EXIT Realty Redefined.",
|
||||
"mainEntity": {
|
||||
"@type": "RealEstateAgent",
|
||||
"name": "{{ SITE_NAME|escapejs }}",
|
||||
"url": "{{ PUBLIC_SITE_URL }}/",
|
||||
"image": "{{ PUBLIC_SITE_URL }}{% static 'images/monica-dhillon-headshot.jpg' %}",
|
||||
"telephone": "{{ CONTACT_PHONE|escapejs }}",
|
||||
"email": "{{ CONTACT_EMAIL|escapejs }}",
|
||||
{% if CONTACT_ADDRESS %}"address": "{{ CONTACT_ADDRESS|escapejs }}",{% endif %}
|
||||
"areaServed": "{{ CONTACT_SERVICE_AREA|escapejs }}",
|
||||
"worksFor": {
|
||||
"@type": "Organization",
|
||||
@@ -52,13 +54,15 @@
|
||||
<div class="container">
|
||||
<div class="row row-50 justify-content-center justify-content-lg-between flex-lg-row-reverse">
|
||||
<div class="col-md-10 col-lg-6 col-xl-5">
|
||||
<h2 class="text-uppercase">Local realtor,<br>personal service</h2>
|
||||
<p class="about-subtitle">I help families buy and sell homes with clear communication and marketing that actually reaches people — not just another postcard blast.</p>
|
||||
<p>This site replaces a costly third-party campaign platform. Visitors reach me through a protected contact form; every inquiry lands in my portal with source tracking so I know which campaigns work.</p>
|
||||
<h2>Meet Monica Dhillon — Your Real Estate Partner in the Western Suburbs</h2>
|
||||
<p class="about-subtitle">Mandeep "Monica" Dhillon is a real estate agent serving Chicago's western suburbs, just forty minutes from downtown. At Exit Realty Redefined, Monica and her team bring years of hands-on experience helping buyers and sellers reach their goals — with a special passion for guiding first-time homebuyers through a process that can feel overwhelming, from mortgages to market values to all the fine print in between.</p>
|
||||
<p>Family values aren't just a tagline here — they're how we treat every client who walks through our door.</p>
|
||||
<p>Monica has been through the homebuying process herself, more than once, so she gets the nerves and the "wait, is this normal?" moments. Her experience spans both sides of the table — buyers, sellers, investors — and properties ranging from $200K starter homes to multi-million dollar deals.</p>
|
||||
<p>Whatever your real estate goals, Monica and the Exit Realty team are ready to help you navigate the market's ups and downs. Questions? Don't be shy — she's always just a call or message away.</p>
|
||||
<a class="button button-lg button-primary button-winona" href="{% url 'public:contact' %}" data-tianji-event="about_cta" data-tianji-event-destination="contact">Work with me</a>
|
||||
</div>
|
||||
<div class="col-md-10 col-lg-6 col-xl-6">
|
||||
<img class="img-responsive" src="{% static 'images/careers-1-570x388.jpg' %}" alt="{{ SITE_NAME }}, EXIT Realty realtor" width="570" height="388"/>
|
||||
<img class="img-responsive" src="{% static 'images/monica-dhillon-headshot.jpg' %}" alt="Professional headshot of Mandeep "Monica" Dhillon, Real Estate Agent, wearing a blue cardigan against a black background." width="816" height="1024"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"url": "{{ PUBLIC_SITE_URL }}/",
|
||||
"telephone": "{{ CONTACT_PHONE|escapejs }}",
|
||||
"email": "{{ CONTACT_EMAIL|escapejs }}",
|
||||
{% if CONTACT_ADDRESS %}"address": "{{ CONTACT_ADDRESS|escapejs }}",{% endif %}
|
||||
"areaServed": "{{ CONTACT_SERVICE_AREA|escapejs }}",
|
||||
"contactPoint": {
|
||||
"@type": "ContactPoint",
|
||||
@@ -67,12 +68,14 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if CONTACT_ADDRESS or CONTACT_SERVICE_AREA %}
|
||||
<div class="layout-bordered-item wow-outer">
|
||||
<div class="layout-bordered-item-inner wow slideInUp">
|
||||
<div class="icon icon-lg mdi mdi-map-marker text-primary"></div>
|
||||
<span class="link-default">{{ CONTACT_SERVICE_AREA }}</span>
|
||||
<span class="link-default">{% if CONTACT_ADDRESS %}{{ CONTACT_ADDRESS }}{% else %}{{ CONTACT_SERVICE_AREA }}{% endif %}</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"image": "{{ PUBLIC_SITE_URL }}{% static 'images/slider-minimal-slide-1-1920x968.jpg' %}",
|
||||
"telephone": "{{ CONTACT_PHONE|escapejs }}",
|
||||
"email": "{{ CONTACT_EMAIL|escapejs }}",
|
||||
{% if CONTACT_ADDRESS %}"address": "{{ CONTACT_ADDRESS|escapejs }}",{% endif %}
|
||||
"description": "{{ SITE_NAME|escapejs }}, EXIT Realty — buying and selling homes with clear advice and multi-channel marketing. {{ CONTACT_SERVICE_AREA|escapejs }}.",
|
||||
"areaServed": "{{ CONTACT_SERVICE_AREA|escapejs }}",
|
||||
"worksFor": {
|
||||
@@ -35,7 +36,7 @@
|
||||
<div class="title-docor-text font-weight-bold title-decorated text-uppercase wow slideInLeft text-white">{{ SITE_NAME }} · MKDRealtor.com</div>
|
||||
</div>
|
||||
<h1 class="text-uppercase text-white font-weight-bold wow-outer"><span class="wow slideInDown" data-wow-delay=".2s">Your Home,<br>Guided Well</span></h1>
|
||||
<p class="text-white wow-outer"><span class="wow slideInDown" data-wow-delay=".35s">EXIT Realty Redefined — buying or selling in the metro area with clear advice, strong marketing, and a team that answers the phone.</span></p>
|
||||
<p class="text-white wow-outer"><span class="wow slideInDown" data-wow-delay=".35s">EXIT Realty Redefined — buying or selling in Chicago's western suburbs with clear advice, strong marketing, and a team that answers the phone.</span></p>
|
||||
<div class="wow-outer button-outer">
|
||||
<a class="button button-md button-primary button-winona wow slideInDown" href="{% url 'public:contact' %}" data-wow-delay=".4s" data-tianji-event="hero_cta" data-tianji-event-destination="contact">Start a conversation</a>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user