Compare commits
19
Commits
master
..
0ba6710faa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ba6710faa | ||
|
|
70181ceda1 | ||
|
|
ea382cf4ba | ||
|
|
2332caca08 | ||
|
|
5596a87cc7 | ||
|
|
3fc73cbf56 | ||
|
|
8630eb7755 | ||
|
|
cd41c75920 | ||
|
|
67acda869a | ||
|
|
c66bfa39f0 | ||
|
|
25bf9c6193 | ||
|
|
16eac8a9e8 | ||
|
|
8b33a179cb | ||
|
|
1033d28e09 | ||
|
|
e70ea3fb96 | ||
|
|
88ee85dda3 | ||
|
|
7ca7721511 | ||
|
|
358231af2e | ||
|
|
eb84d01c03 |
@@ -34,9 +34,6 @@ EMAIL_HOST_PASSWORD=
|
|||||||
EMAIL_PORT=2525
|
EMAIL_PORT=2525
|
||||||
EMAIL_USE_TLS=true
|
EMAIL_USE_TLS=true
|
||||||
# DEFAULT_FROM_EMAIL=AI ML Operations, LLC <info@aimloperations.com>
|
# DEFAULT_FROM_EMAIL=AI ML Operations, LLC <info@aimloperations.com>
|
||||||
# Absolute origin for email logo / footer links
|
|
||||||
PUBLIC_SITE_URL=https://aimloperations.com
|
|
||||||
# DEFAULT_FROM_EMAIL=AI ML Operations, LLC <info@aimloperations.com>
|
|
||||||
|
|
||||||
# Stripe (test keys from https://dashboard.stripe.com/test/apikeys)
|
# Stripe (test keys from https://dashboard.stripe.com/test/apikeys)
|
||||||
STRIPE_SECRET_KEY=
|
STRIPE_SECRET_KEY=
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ EMAIL_HOST_PASSWORD=replace-with-smtp-password
|
|||||||
EMAIL_PORT=2525
|
EMAIL_PORT=2525
|
||||||
EMAIL_USE_TLS=true
|
EMAIL_USE_TLS=true
|
||||||
# DEFAULT_FROM_EMAIL=AI ML Operations, LLC <info@aimloperations.com>
|
# DEFAULT_FROM_EMAIL=AI ML Operations, LLC <info@aimloperations.com>
|
||||||
PUBLIC_SITE_URL=https://aimloperations.com
|
|
||||||
|
|
||||||
# Stripe (live keys from https://dashboard.stripe.com/apikeys)
|
# Stripe (live keys from https://dashboard.stripe.com/apikeys)
|
||||||
# Secret key starts with sk_live_; publishable with pk_live_
|
# Secret key starts with sk_live_; publishable with pk_live_
|
||||||
|
|||||||
@@ -204,8 +204,6 @@ DEFAULT_FROM_EMAIL = env(
|
|||||||
"DEFAULT_FROM_EMAIL",
|
"DEFAULT_FROM_EMAIL",
|
||||||
"AI ML Operations, LLC <info@aimloperations.com>",
|
"AI ML Operations, LLC <info@aimloperations.com>",
|
||||||
)
|
)
|
||||||
# Absolute site origin for email logo / footer links (no trailing slash).
|
|
||||||
PUBLIC_SITE_URL = env("PUBLIC_SITE_URL", "https://aimloperations.com").rstrip("/")
|
|
||||||
|
|
||||||
# Stripe (invoices / subscriptions — customers pay us)
|
# Stripe (invoices / subscriptions — customers pay us)
|
||||||
STRIPE_SECRET_KEY = env("STRIPE_SECRET_KEY", "")
|
STRIPE_SECRET_KEY = env("STRIPE_SECRET_KEY", "")
|
||||||
|
|||||||
@@ -284,16 +284,14 @@ def send_pay_link_email(
|
|||||||
"DEFAULT_FROM_EMAIL",
|
"DEFAULT_FROM_EMAIL",
|
||||||
"AI ML Operations, LLC <info@aimloperations.com>",
|
"AI ML Operations, LLC <info@aimloperations.com>",
|
||||||
)
|
)
|
||||||
from public.email_branding import email_brand_context
|
context = {
|
||||||
|
"customer_name": customer_name,
|
||||||
context = email_brand_context(
|
"description": description,
|
||||||
customer_name=customer_name,
|
"amount_dollars": f"{amount_dollars:.2f}",
|
||||||
description=description,
|
"pay_url": pay_url,
|
||||||
amount_dollars=f"{amount_dollars:.2f}",
|
"kind": kind,
|
||||||
pay_url=pay_url,
|
"subject": subject,
|
||||||
kind=kind,
|
}
|
||||||
subject=subject,
|
|
||||||
)
|
|
||||||
html_content = get_template("emails/invoice_pay_link.html").render(context)
|
html_content = get_template("emails/invoice_pay_link.html").render(context)
|
||||||
text_content = get_template("emails/invoice_pay_link.txt").render(context)
|
text_content = get_template("emails/invoice_pay_link.txt").render(context)
|
||||||
msg = EmailMultiAlternatives(subject, text_content, from_email, [to_email])
|
msg = EmailMultiAlternatives(subject, text_content, from_email, [to_email])
|
||||||
|
|||||||
@@ -1,26 +1,48 @@
|
|||||||
{% extends "emails/base_email.html" %}
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
{% block title %}{{ subject }}{% endblock %}
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
{% block content %}
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<p style="margin:0 0 16px;color:#e0e0e0;">Hello {{ customer_name }},</p>
|
<title>{{ subject }}</title>
|
||||||
{% if kind == "subscription" %}
|
<style>
|
||||||
<p style="margin:0 0 16px;color:#e0e0e0;">Please complete checkout to start your recurring payment:</p>
|
body { margin: 0; padding: 0; font-family: Arial, sans-serif; background-color: #f4f4f4; }
|
||||||
{% else %}
|
.email-container { max-width: 600px; margin: 0 auto; background-color: #ffffff; border: 1px solid #dddddd; }
|
||||||
<p style="margin:0 0 16px;color:#e0e0e0;">You have a new invoice ready for payment:</p>
|
.header { background-color: #0b3d5c; color: #ffffff; padding: 20px; text-align: center; }
|
||||||
{% endif %}
|
.content { padding: 24px; color: #333333; font-size: 15px; line-height: 1.5; }
|
||||||
<p style="margin:0 0 8px;color:#e0e0e0;"><strong>{{ description }}</strong></p>
|
.btn {
|
||||||
<p class="amount" style="font-size:22px;font-weight:700;color:#00f3ff;margin:12px 0;">${{ amount_dollars }} USD</p>
|
display: inline-block; margin: 20px 0; padding: 12px 24px;
|
||||||
<p style="margin:24px 0;">
|
background-color: #0b3d5c; color: #ffffff !important; text-decoration: none;
|
||||||
<a class="email-btn" href="{{ pay_url }}" style="display:inline-block;padding:14px 28px;background-color:#00f3ff;color:#0a0a0a !important;text-decoration:none;border-radius:30px;font-weight:700;font-size:14px;letter-spacing:0.5px;text-transform:uppercase;">
|
border-radius: 4px; font-weight: bold;
|
||||||
{% if kind == "subscription" %}Complete checkout{% else %}Pay invoice{% endif %}
|
}
|
||||||
</a>
|
.footer { background-color: #f4f4f4; color: #777777; text-align: center; padding: 12px; font-size: 12px; }
|
||||||
</p>
|
.amount { font-size: 22px; font-weight: bold; margin: 12px 0; }
|
||||||
<p class="muted" style="color:#a0a0a0;font-size:13px;line-height:1.5;margin:0 0 16px;">
|
</style>
|
||||||
Or open this link:<br>
|
</head>
|
||||||
<a href="{{ pay_url }}" style="color:#00f3ff;word-break:break-all;">{{ pay_url }}</a>
|
<body>
|
||||||
</p>
|
<div class="email-container">
|
||||||
<p style="margin:0;color:#e0e0e0;">Thank you,<br>{{ brand_legal|default:"AI ML Operations, LLC" }}</p>
|
<div class="header">
|
||||||
{% endblock %}
|
<h1>AI ML Operations</h1>
|
||||||
|
</div>
|
||||||
{% block footer_note %}This is an automated message. Please do not reply to this email.{% endblock %}
|
<div class="content">
|
||||||
|
<p>Hello {{ customer_name }},</p>
|
||||||
|
{% if kind == "subscription" %}
|
||||||
|
<p>Please complete checkout to start your recurring payment:</p>
|
||||||
|
{% else %}
|
||||||
|
<p>You have a new invoice ready for payment:</p>
|
||||||
|
{% endif %}
|
||||||
|
<p><strong>{{ description }}</strong></p>
|
||||||
|
<p class="amount">${{ amount_dollars }} USD</p>
|
||||||
|
<p>
|
||||||
|
<a class="btn" href="{{ pay_url }}">
|
||||||
|
{% if kind == "subscription" %}Complete checkout{% else %}Pay invoice{% endif %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p style="font-size: 13px; color: #666;">Or open this link:<br>{{ pay_url }}</p>
|
||||||
|
<p>Thank you,<br>AI ML Operations, LLC</p>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<p>This is an automated message. Please do not reply to this email.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
@@ -9,8 +9,4 @@ Amount: ${{ amount_dollars }} USD
|
|||||||
{{ pay_url }}
|
{{ pay_url }}
|
||||||
|
|
||||||
Thank you,
|
Thank you,
|
||||||
{{ brand_legal|default:"AI ML Operations, LLC" }}
|
AI ML Operations, LLC
|
||||||
|
|
||||||
—
|
|
||||||
{{ site_url|default:"https://aimloperations.com" }}
|
|
||||||
Forward-deployed AI engineering
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from django.conf import settings
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.mail import EmailMultiAlternatives
|
from django.core.mail import EmailMultiAlternatives
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
@@ -6,7 +5,6 @@ from django.template.loader import get_template
|
|||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .email_branding import email_brand_context
|
|
||||||
from .models import Contact, EmailMessage, PageVisit
|
from .models import Contact, EmailMessage, PageVisit
|
||||||
|
|
||||||
|
|
||||||
@@ -38,12 +36,8 @@ def send_emails(modeladmin, request, queryset):
|
|||||||
for email in queryset:
|
for email in queryset:
|
||||||
success_count: int = 0
|
success_count: int = 0
|
||||||
try:
|
try:
|
||||||
from_email = getattr(
|
from_email = "AI ML Operations, LLC <info@aimloperations.com>"
|
||||||
settings,
|
d = {"title": email.subject, "content": email.body}
|
||||||
"DEFAULT_FROM_EMAIL",
|
|
||||||
"AI ML Operations, LLC <info@aimloperations.com>",
|
|
||||||
)
|
|
||||||
d = email_brand_context(title=email.subject, content=email.body)
|
|
||||||
|
|
||||||
html_content = get_template("emails/marketing_email.html").render(d)
|
html_content = get_template("emails/marketing_email.html").render(d)
|
||||||
text_content = get_template("emails/marketing_email.txt").render(d)
|
text_content = get_template("emails/marketing_email.txt").render(d)
|
||||||
@@ -83,11 +77,8 @@ class EmailMessageAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
def preview_email(self, request, pk):
|
def preview_email(self, request, pk):
|
||||||
email_instance = get_object_or_404(EmailMessage, pk=pk)
|
email_instance = get_object_or_404(EmailMessage, pk=pk)
|
||||||
context = email_brand_context(
|
context = {"title": email_instance.subject, "content": email_instance.body}
|
||||||
title=email_instance.subject,
|
return TemplateResponse(request, "public/preview_email.html", context)
|
||||||
content=email_instance.body,
|
|
||||||
)
|
|
||||||
return TemplateResponse(request, "emails/marketing_email.html", context)
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(PageVisit)
|
@admin.register(PageVisit)
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
"""Shared context for branded HTML/text emails."""
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.contrib.staticfiles.storage import staticfiles_storage
|
|
||||||
|
|
||||||
|
|
||||||
def email_brand_context(**extra):
|
|
||||||
site_url = getattr(settings, "PUBLIC_SITE_URL", "https://aimloperations.com").rstrip(
|
|
||||||
"/"
|
|
||||||
)
|
|
||||||
logo_path = staticfiles_storage.url("public/img/logo.png")
|
|
||||||
if logo_path.startswith("http://") or logo_path.startswith("https://"):
|
|
||||||
logo_url = logo_path
|
|
||||||
else:
|
|
||||||
logo_url = f"{site_url}{logo_path}"
|
|
||||||
return {
|
|
||||||
"site_url": site_url,
|
|
||||||
"logo_url": logo_url,
|
|
||||||
"brand_name": "AI ML Operations",
|
|
||||||
"brand_legal": "AI ML Operations, LLC",
|
|
||||||
**extra,
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta name="color-scheme" content="dark">
|
|
||||||
<meta name="supported-color-schemes" content="dark">
|
|
||||||
<title>{% block title %}{{ brand_name|default:"AI ML Operations" }}{% endblock %}</title>
|
|
||||||
<!--[if mso]>
|
|
||||||
<style type="text/css">
|
|
||||||
body, table, td { font-family: Arial, Helvetica, sans-serif !important; }
|
|
||||||
</style>
|
|
||||||
<![endif]-->
|
|
||||||
<style type="text/css">
|
|
||||||
body, table, td, a {
|
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
-ms-text-size-adjust: 100%;
|
|
||||||
}
|
|
||||||
table, td {
|
|
||||||
mso-table-lspace: 0pt;
|
|
||||||
mso-table-rspace: 0pt;
|
|
||||||
}
|
|
||||||
img {
|
|
||||||
border: 0;
|
|
||||||
height: auto;
|
|
||||||
line-height: 100%;
|
|
||||||
outline: none;
|
|
||||||
text-decoration: none;
|
|
||||||
-ms-interpolation-mode: bicubic;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 0 !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
width: 100% !important;
|
|
||||||
background-color: #0a0a0a;
|
|
||||||
color: #e0e0e0;
|
|
||||||
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
||||||
}
|
|
||||||
a { color: #00f3ff; }
|
|
||||||
.email-btn {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 14px 28px;
|
|
||||||
background-color: #00f3ff;
|
|
||||||
color: #0a0a0a !important;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 30px;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 14px;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
.muted { color: #a0a0a0; font-size: 13px; line-height: 1.5; }
|
|
||||||
.amount { font-size: 22px; font-weight: 700; color: #00f3ff; margin: 12px 0; }
|
|
||||||
.field-label { color: #a0a0a0; font-size: 12px; text-transform: uppercase; letter-spacing: 0.6px; margin: 0 0 4px; }
|
|
||||||
.field-value { color: #e0e0e0; font-size: 15px; margin: 0 0 16px; line-height: 1.5; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body style="margin:0;padding:0;background-color:#0a0a0a;">
|
|
||||||
{% block preheader %}{% endblock %}
|
|
||||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="background-color:#0a0a0a;">
|
|
||||||
<tr>
|
|
||||||
<td align="center" style="padding:32px 16px;">
|
|
||||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="max-width:600px;background-color:#1a1a1a;border:1px solid rgba(255,255,255,0.1);">
|
|
||||||
<tr>
|
|
||||||
<td align="center" style="padding:28px 24px 20px;border-bottom:1px solid rgba(0,243,255,0.35);">
|
|
||||||
{% if logo_url %}
|
|
||||||
<a href="{{ site_url|default:'https://aimloperations.com' }}" style="text-decoration:none;">
|
|
||||||
<img src="{{ logo_url }}" alt="{{ brand_name|default:'AI ML Operations' }}" width="220" style="display:block;width:220px;max-width:80%;height:auto;">
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
<p style="margin:0;font-size:18px;font-weight:700;letter-spacing:1px;color:#e0e0e0;text-transform:uppercase;">
|
|
||||||
{{ brand_name|default:"AI ML Operations" }}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
{% block header_extra %}{% endblock %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="height:3px;line-height:3px;font-size:0;background-color:#bc13fe;"> </td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="padding:28px 28px 8px;color:#e0e0e0;font-size:15px;line-height:1.6;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;">
|
|
||||||
{% block content %}{% endblock %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="padding:8px 28px 28px;color:#a0a0a0;font-size:12px;line-height:1.5;text-align:center;border-top:1px solid rgba(255,255,255,0.08);font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;">
|
|
||||||
{% block footer %}
|
|
||||||
<p style="margin:16px 0 8px;color:#a0a0a0;">
|
|
||||||
{% block footer_note %}{% endblock %}
|
|
||||||
</p>
|
|
||||||
<p style="margin:0 0 4px;color:#a0a0a0;">
|
|
||||||
© {% now "Y" %} {{ brand_legal|default:"AI ML Operations, LLC" }}. All rights reserved.
|
|
||||||
</p>
|
|
||||||
<p style="margin:0;color:#a0a0a0;">
|
|
||||||
<a href="{{ site_url|default:'https://aimloperations.com' }}" style="color:#00f3ff;text-decoration:none;">aimloperations.com</a>
|
|
||||||
· Forward-deployed AI engineering
|
|
||||||
</p>
|
|
||||||
{% endblock %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,21 +1,110 @@
|
|||||||
{% extends "emails/base_email.html" %}
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>New Feedback Submission</title>
|
||||||
|
<style>
|
||||||
|
/* Basic reset for email clients */
|
||||||
|
body, table, td, a {
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
table, td {
|
||||||
|
mso-table-lspace: 0pt;
|
||||||
|
mso-table-rspace: 0pt;
|
||||||
|
}
|
||||||
|
img {
|
||||||
|
border: 0;
|
||||||
|
height: auto;
|
||||||
|
line-height: 100%;
|
||||||
|
outline: none;
|
||||||
|
text-decoration: none;
|
||||||
|
-ms-interpolation-mode: bicubic;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
}
|
||||||
|
.email-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
background-color: #007BFF;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 20px;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
color: #777777;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.feedback-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.feedback-text {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" align="center">
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<!-- Email Container -->
|
||||||
|
<div class="email-container">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="header">
|
||||||
|
<h1>New Contact Request</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% block title %}New Contact Request{% endblock %}
|
<!-- Content -->
|
||||||
|
<div class="content">
|
||||||
|
<p>Hello,</p>
|
||||||
|
<p>A new feedback item has been submitted. Here are the details:</p>
|
||||||
|
|
||||||
{% block content %}
|
<!-- Feedback Title -->
|
||||||
<p style="margin:0 0 16px;color:#e0e0e0;">Hello,</p>
|
<div class="feedback-title">
|
||||||
<p style="margin:0 0 24px;color:#e0e0e0;">A new contact request was submitted on the site.</p>
|
Subject: <strong>{{ subject }}</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p class="field-label" style="color:#a0a0a0;font-size:12px;text-transform:uppercase;letter-spacing:0.6px;margin:0 0 4px;">Subject</p>
|
<!-- Email -->
|
||||||
<p class="field-value" style="color:#e0e0e0;font-size:15px;margin:0 0 16px;"><strong>{{ subject }}</strong></p>
|
<div class="feedback-title">
|
||||||
|
Email: <strong>{{ email }}</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
<p class="field-label" style="color:#a0a0a0;font-size:12px;text-transform:uppercase;letter-spacing:0.6px;margin:0 0 4px;">From</p>
|
<!-- Feedback Text -->
|
||||||
<p class="field-value" style="color:#e0e0e0;font-size:15px;margin:0 0 16px;">
|
<div class="feedback-text">
|
||||||
<a href="mailto:{{ email }}" style="color:#00f3ff;text-decoration:none;">{{ email }}</a>
|
<strong>Message:</strong><br>
|
||||||
</p>
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
|
||||||
<p class="field-label" style="color:#a0a0a0;font-size:12px;text-transform:uppercase;letter-spacing:0.6px;margin:0 0 4px;">Message</p>
|
<p>Thank you for your attention.</p>
|
||||||
<p class="field-value" style="color:#e0e0e0;font-size:15px;margin:0 0 16px;white-space:pre-wrap;">{{ message }}</p>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block footer_note %}This is an automated message. Please do not reply to this email.{% endblock %}
|
<!-- Footer -->
|
||||||
|
<div class="footer">
|
||||||
|
<p>This is an automated message. Please do not reply to this email.</p>
|
||||||
|
<p>© 2025 AI ML Operations, LLC. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,12 +1,3 @@
|
|||||||
New Contact Request — {{ brand_legal|default:"AI ML Operations, LLC" }}
|
New Contact Request for AI ML Operations, LLC
|
||||||
|
|
||||||
Subject: {{ subject }}
|
"New Contact. {{ subject }} from {{ email }}. {{ message }}"
|
||||||
From: {{ email }}
|
|
||||||
|
|
||||||
Message:
|
|
||||||
{{ message }}
|
|
||||||
|
|
||||||
—
|
|
||||||
{{ brand_legal|default:"AI ML Operations, LLC" }}
|
|
||||||
{{ site_url|default:"https://aimloperations.com" }}
|
|
||||||
Forward-deployed AI engineering
|
|
||||||
@@ -1,15 +1,61 @@
|
|||||||
{% extends "emails/base_email.html" %}
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Email Template</title>
|
||||||
|
<!-- Materialize CSS -->
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.email-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
background-color: #37474f;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 20px;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
background-color: #333;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="email-container">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="header">
|
||||||
|
<h4>{{ title | safe }}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% block title %}{{ title }}{% endblock %}
|
<!-- Content -->
|
||||||
|
<div class="content">
|
||||||
|
{{ content | safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% block header_extra %}
|
<!-- Footer -->
|
||||||
{% if title %}
|
<div class="footer">
|
||||||
<p style="margin:16px 0 0;font-size:18px;font-weight:600;color:#e0e0e0;line-height:1.4;">{{ title|safe }}</p>
|
<p>© 2025 AI ML Operations, LLC. All rights reserved.</p>
|
||||||
{% endif %}
|
</div>
|
||||||
{% endblock %}
|
</div>
|
||||||
|
</body>
|
||||||
{% block content %}
|
</html>
|
||||||
{{ content|safe }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block footer_note %}{% endblock %}
|
|
||||||
@@ -1,8 +1,3 @@
|
|||||||
{{ title }}
|
{{ subject }}
|
||||||
|
|
||||||
{{ content }}
|
{{ content }}
|
||||||
|
|
||||||
—
|
|
||||||
{{ brand_legal|default:"AI ML Operations, LLC" }}
|
|
||||||
{{ site_url|default:"https://aimloperations.com" }}
|
|
||||||
Forward-deployed AI engineering
|
|
||||||
|
|||||||
@@ -1 +1,61 @@
|
|||||||
{% extends "emails/marketing_email.html" %}
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Email Template</title>
|
||||||
|
<!-- Materialize CSS -->
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.email-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
background-color: #37474f;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.content {
|
||||||
|
padding: 20px;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
background-color: #333;
|
||||||
|
padding: 10px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="email-container">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="header">
|
||||||
|
<h4>{{ title | safe }}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<div class="content">
|
||||||
|
{{ content | safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<div class="footer">
|
||||||
|
<p>© 2025 AI ML Operations, LLC. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -14,25 +14,20 @@ from django.urls import reverse
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
|
||||||
from .email_branding import email_brand_context
|
|
||||||
from .forms import FormWithCaptcha
|
from .forms import FormWithCaptcha
|
||||||
from .middleware import get_session_utm
|
from .middleware import get_session_utm
|
||||||
from .models import Contact, EmailMessage, PageVisit
|
from .models import Contact, EmailMessage, PageVisit
|
||||||
from .traffic import TRAFFIC_TYPE_LABELS, TrafficType
|
from .traffic import TRAFFIC_TYPE_LABELS, TrafficType
|
||||||
|
|
||||||
def send_contact_email(email, subject, message):
|
def send_contact_email(email, subject, message):
|
||||||
mail_subject = "New Contact Request for AI ML Operations, LLC"
|
subject = "New Contact Request for AI ML Operations, LLC"
|
||||||
from_email = getattr(
|
from_email = "ryan@aimloperations.com"
|
||||||
settings,
|
to="ryan@aimloperations.com"
|
||||||
"DEFAULT_FROM_EMAIL",
|
d = {"subject": subject, "message": message, "email": email}
|
||||||
"AI ML Operations, LLC <info@aimloperations.com>",
|
html_content = get_template(r'emails/contact_email.html').render(d)
|
||||||
)
|
text_content = get_template(r'emails/contact_email.txt').render(d)
|
||||||
to = "ryan@aimloperations.com"
|
|
||||||
d = email_brand_context(subject=subject, message=message, email=email)
|
|
||||||
html_content = get_template("emails/contact_email.html").render(d)
|
|
||||||
text_content = get_template("emails/contact_email.txt").render(d)
|
|
||||||
|
|
||||||
msg = EmailMultiAlternatives(mail_subject, text_content, from_email, [to])
|
msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
|
||||||
msg.attach_alternative(html_content, "text/html")
|
msg.attach_alternative(html_content, "text/html")
|
||||||
msg.send(fail_silently=True)
|
msg.send(fail_silently=True)
|
||||||
|
|
||||||
@@ -86,11 +81,13 @@ def terms_of_service(request):
|
|||||||
@login_required
|
@login_required
|
||||||
def preview_email(request, pk):
|
def preview_email(request, pk):
|
||||||
email_instance = get_object_or_404(EmailMessage, pk=pk)
|
email_instance = get_object_or_404(EmailMessage, pk=pk)
|
||||||
context = email_brand_context(
|
context = {
|
||||||
title=email_instance.subject,
|
"title":email_instance.subject,
|
||||||
content=email_instance.body,
|
"content":email_instance.body
|
||||||
|
}
|
||||||
|
return render(
|
||||||
|
request, 'public/preview_email.html', context
|
||||||
)
|
)
|
||||||
return render(request, "emails/marketing_email.html", context)
|
|
||||||
|
|
||||||
def contact(request):
|
def contact(request):
|
||||||
errors = ''
|
errors = ''
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
# Email previews
|
|
||||||
|
|
||||||
Static renders of branded HTML emails (dark neon site UX: `#0a0a0a` / `#1a1a1a` / `#00f3ff` / `#bc13fe`).
|
|
||||||
|
|
||||||
| Template | Screenshot |
|
|
||||||
|----------|------------|
|
|
||||||
| Contact notify | [contact.png](contact.png) |
|
|
||||||
| Marketing | [marketing.png](marketing.png) |
|
|
||||||
| Invoice / pay link | [invoice.png](invoice.png) |
|
|
||||||
|
|
||||||
Shared base: `public/templates/emails/base_email.html`.
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 48 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
Reference in New Issue
Block a user