`, bgColor, bgColor, accentColor, emailFontStack, number, emailFontStack, titleColor, title, emailFontStack, description)
}
// ──────────────────────────────────────────────────────────────────────────────
// Email template methods
// ──────────────────────────────────────────────────────────────────────────────
// SendWelcomeEmail sends a welcome email with verification code
func (s *EmailService) SendWelcomeEmail(to, firstName, code string) error {
subject := "Welcome to honeyDue - Verify Your Email"
name := firstName
if name == "" {
name = "there"
}
bodyContent := fmt.Sprintf(`
%s
Hi %s,
Welcome! Verify your email to get started:
%s
If you didn't create a honeyDue account, you can safely ignore this email.
%s`,
emailHeader("Verify your email"),
emailFontStack, name,
emailFontStack,
emailCodeBox(code, "Expires in 24 hours"),
emailFontStack,
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`Welcome to honeyDue!
Hi %s,
Welcome! Verify your email to get started.
Your verification code: %s
This code expires in 24 hours.
If you didn't create a honeyDue account, you can safely ignore this email.
- The honeyDue Team
`, name, code)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// SendAppleWelcomeEmail sends a welcome email for Apple Sign In users (no verification needed)
func (s *EmailService) SendAppleWelcomeEmail(to, firstName string) error {
subject := "Welcome to honeyDue!"
name := firstName
if name == "" {
name = "there"
}
features := emailFeatureItem("🏠", "Manage Properties", "Track all your homes and rentals in one place", "#EDF2ED", "#2D3436") +
emailFeatureItem("✅", "Task Management", "Never miss maintenance with smart scheduling", "#EDF2ED", "#2D3436") +
emailFeatureItem("👷", "Contractor Directory", "Keep your trusted pros organized", "#FDF3EE", "#2D3436") +
emailFeatureItem("📄", "Document Storage", "Store warranties, manuals, and important records", "#FEF3C7", "#2D3436")
bodyContent := fmt.Sprintf(`
%s
Hi %s, your account is ready! Here's what you can do:
%s`,
emailHeader("Welcome to honeyDue!"),
emailFontStack, name,
emailCalloutBox(features),
emailButton("Open honeyDue", "honeydue://home", "#C4856A"),
emailFontStack,
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`Welcome to honeyDue!
Hi %s,
Your account is ready! Here's what you can do:
- Manage Properties: Track all your homes and rentals in one place
- Task Management: Never miss maintenance with smart scheduling
- Contractor Directory: Keep your trusted pros organized
- Document Storage: Store warranties, manuals, and important records
Questions? Reach out anytime at honeydue@treymail.com
- The honeyDue Team
`, name)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// SendGoogleWelcomeEmail sends a welcome email for Google Sign In users (no verification needed)
func (s *EmailService) SendGoogleWelcomeEmail(to, firstName string) error {
subject := "Welcome to honeyDue!"
name := firstName
if name == "" {
name = "there"
}
features := emailFeatureItem("🏠", "Manage Properties", "Track all your homes and rentals in one place", "#EDF2ED", "#2D3436") +
emailFeatureItem("✅", "Task Management", "Never miss maintenance with smart scheduling", "#EDF2ED", "#2D3436") +
emailFeatureItem("👷", "Contractor Directory", "Keep your trusted pros organized", "#FDF3EE", "#2D3436") +
emailFeatureItem("📄", "Document Storage", "Store warranties, manuals, and important records", "#FEF3C7", "#2D3436")
bodyContent := fmt.Sprintf(`
%s
Hi %s, your account is ready! Here's what you can do:
%s`,
emailHeader("Welcome to honeyDue!"),
emailFontStack, name,
emailCalloutBox(features),
emailButton("Open honeyDue", "honeydue://home", "#C4856A"),
emailFontStack,
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`Welcome to honeyDue!
Hi %s,
Your account is ready! Here's what you can do:
- Manage Properties: Track all your homes and rentals in one place
- Task Management: Never miss maintenance with smart scheduling
- Contractor Directory: Keep your trusted pros organized
- Document Storage: Store warranties, manuals, and important records
Questions? Reach out anytime at honeydue@treymail.com
- The honeyDue Team
`, name)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// SendPostVerificationEmail sends a welcome email after user verifies their email address
func (s *EmailService) SendPostVerificationEmail(to, firstName string) error {
subject := "You're All Set! Getting Started with honeyDue"
name := firstName
if name == "" {
name = "there"
}
tips := emailTipCard("1", "Add Your Property", "Start by adding your home. You can manage multiple properties and share access with family.", "#6B8F71", "#EDF2ED", "#2D3436") +
emailTipCard("2", "Set Up Recurring Tasks", "Create tasks for HVAC filter changes, gutter cleaning, lawn care. We'll remind you when they're due.", "#C4856A", "#FDF3EE", "#2D3436") +
emailTipCard("3", "Track Maintenance History", "Complete tasks with notes and photos. Invaluable for warranty claims or when selling your home.", "#D97706", "#FEF3C7", "#2D3436") +
emailTipCard("4", "Store Important Documents", "Upload warranties, manuals, insurance policies. Find them instantly instead of digging through drawers.", "#6B8F71", "#F2EFE9", "#2D3436") +
emailTipCard("5", "Save Your Contractors", "Keep your trusted plumber, electrician, and other pros organized and one tap away.", "#C4856A", "#FDF3EE", "#2D3436")
bodyContent := fmt.Sprintf(`
%s
Hi %s, your email is verified and you're ready to go! Here's how to get the most out of honeyDue:
%s`,
emailHeader("You're all set!"),
emailFontStack, name,
tips,
emailButton("Get Started", "honeydue://home", "#C4856A"),
emailFontStack,
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`You're All Set!
Hi %s,
Your email is verified and you're ready to go! Here's how to get the most out of honeyDue:
1. ADD YOUR PROPERTY
Start by adding your home. You can manage multiple properties and share access with family.
2. SET UP RECURRING TASKS
Create tasks for HVAC filter changes, gutter cleaning, lawn care. We'll remind you when they're due.
3. TRACK MAINTENANCE HISTORY
Complete tasks with notes and photos. Invaluable for warranty claims or when selling your home.
4. STORE IMPORTANT DOCUMENTS
Upload warranties, manuals, insurance policies. Find them instantly instead of digging through drawers.
5. SAVE YOUR CONTRACTORS
Keep your trusted plumber, electrician, and other pros organized and one tap away.
Questions? honeydue@treymail.com
- The honeyDue Team
`, name)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// SendVerificationEmail sends an email verification code
func (s *EmailService) SendVerificationEmail(to, firstName, code string) error {
subject := "honeyDue - Verify Your Email"
name := firstName
if name == "" {
name = "there"
}
bodyContent := fmt.Sprintf(`
%s
Hi %s,
Here's your verification code:
%s
If you didn't request this, you can safely ignore this email.
%s`,
emailHeader("Verify your email"),
emailFontStack, name,
emailFontStack,
emailCodeBox(code, "Expires in 24 hours"),
emailFontStack,
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`Verify Your Email
Hi %s,
Here's your verification code: %s
This code expires in 24 hours.
If you didn't request this, you can safely ignore this email.
- The honeyDue Team
`, name, code)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// SendPasswordResetEmail sends a password reset email
func (s *EmailService) SendPasswordResetEmail(to, firstName, code string) error {
subject := "honeyDue - Password Reset Request"
name := firstName
if name == "" {
name = "there"
}
bodyContent := fmt.Sprintf(`
%s
Hi %s,
Use this code to reset your password:
%s
%s
%s`,
emailHeader("Reset your password"),
emailFontStack, name,
emailFontStack,
emailCodeBox(code, "Expires in 15 minutes"),
emailAlertBox("Didn't request this?", "If you didn't request a password reset, please ignore this email. Your password will remain unchanged.", "#C4856A", "#FDF3EE", "#A06B52"),
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`Password Reset Request
Hi %s,
Use this code to reset your password: %s
This code expires in 15 minutes.
If you didn't request a password reset, please ignore this email. Your password will remain unchanged.
- The honeyDue Team
`, name, code)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// SendPasswordChangedEmail sends a password changed confirmation email
func (s *EmailService) SendPasswordChangedEmail(to, firstName string) error {
subject := "honeyDue - Your Password Has Been Changed"
name := firstName
if name == "" {
name = "there"
}
changeTime := time.Now().UTC().Format("January 2, 2006 at 3:04 PM UTC")
bodyContent := fmt.Sprintf(`
%s
Hi %s,
Your password was successfully changed on %s.
%s
%s`,
emailHeader("Password changed"),
emailFontStack, name,
emailFontStack, changeTime,
emailAlertBox("Wasn't you?", "If you didn't make this change, contact us immediately at honeydue@treymail.com or reset your password right away.", "#C4856A", "#FDF3EE", "#A06B52"),
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`Password Changed
Hi %s,
Your password was successfully changed on %s.
If you didn't make this change, contact us immediately at honeydue@treymail.com or reset your password right away.
- The honeyDue Team
`, name, changeTime)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// SendTaskCompletedEmail sends an email notification when a task is completed
// images parameter is optional - pass nil or empty slice if no images
func (s *EmailService) SendTaskCompletedEmail(to, recipientName, taskTitle, completedByName, residenceName string, images []EmbeddedImage) error {
subject := fmt.Sprintf("honeyDue - Task Completed: %s", taskTitle)
name := recipientName
if name == "" {
name = "there"
}
completedTime := time.Now().UTC().Format("January 2, 2006 at 3:04 PM")
// Build images HTML section if images are provided
imagesHTML := ""
imagesText := ""
if len(images) > 0 {
photoLabel := "Photo"
if len(images) > 1 {
photoLabel = "Photos"
}
imagesHTML = fmt.Sprintf(`
%s`,
emailHeader("Tasks report"),
emailFontStack, name, residenceName,
emailCalloutBox(statRow),
emailButton("Open honeyDue", "honeydue://tasks", "#6B8F71"),
emailFontStack,
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`Tasks Report for %s
Hi %s,
Here's your report for %s:
Total: %d | Completed: %d | Pending: %d | Overdue: %d
The full report is attached as a PDF.
- The honeyDue Team
`, residenceName, name, residenceName, totalTasks, completed, pending, overdue)
// Create filename with timestamp
filename := fmt.Sprintf("tasks_report_%s_%s.pdf",
residenceName,
time.Now().Format("2006-01-02"),
)
attachment := &EmailAttachment{
Filename: filename,
ContentType: "application/pdf",
Data: pdfData,
}
return s.SendEmailWithAttachment(to, subject, htmlBody, textBody, attachment)
}
// SendNoResidenceOnboardingEmail sends an onboarding email to users who haven't created a residence
func (s *EmailService) SendNoResidenceOnboardingEmail(to, firstName, baseURL, trackingID string) error {
subject := "Get started with honeyDue - Add your first property"
name := firstName
if name == "" {
name = "there"
}
trackingPixel := fmt.Sprintf(``, baseURL, trackingID)
features := emailFeatureItem("🏠", "Track All Your Homes", "Manage single-family homes, apartments, or investment properties", "#EDF2ED", "#2D3436") +
emailFeatureItem("📅", "Never Miss Maintenance", "Set up recurring tasks with smart reminders", "#EDF2ED", "#2D3436") +
emailFeatureItem("📄", "Store Documents", "Keep warranties, manuals, and records in one place", "#FEF3C7", "#2D3436") +
emailFeatureItem("👷", "Manage Contractors", "Keep your trusted pros organized and accessible", "#FDF3EE", "#2D3436")
bodyContent := fmt.Sprintf(`
%s
Hi %s, you haven't added your first property yet. It only takes a minute and unlocks everything honeyDue has to offer:
%s
%s
Open the app and tap + to get started.
%s
%s`,
emailHeader("Add your first property"),
emailFontStack, name,
emailCalloutBox(features),
emailButton("Add Your First Property", "honeydue://add-property", "#C4856A"),
emailFontStack,
trackingPixel,
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`Add Your First Property
Hi %s,
You haven't added your first property yet. It only takes a minute!
- Track all your homes
- Never miss maintenance
- Store important documents
- Manage contractors
Open the honeyDue app and tap + to get started.
- The honeyDue Team
`, name)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// SendNoTasksOnboardingEmail sends an onboarding email to users who have a property but no tasks
func (s *EmailService) SendNoTasksOnboardingEmail(to, firstName, baseURL, trackingID string) error {
subject := "Stay on top of home maintenance with honeyDue"
name := firstName
if name == "" {
name = "there"
}
trackingPixel := fmt.Sprintf(``, baseURL, trackingID)
features := emailFeatureItem("🌡️", "HVAC Filter Replacement", "Monthly or quarterly", "#EDF2ED", "#2D3436") +
emailFeatureItem("💧", "Water Heater Flush", "Annually", "#EDF2ED", "#2D3436") +
emailFeatureItem("🌿", "Lawn Care", "Weekly or bi-weekly", "#EDF2ED", "#2D3436") +
emailFeatureItem("🕶", "Gutter Cleaning", "Seasonal", "#FEF3C7", "#2D3436") +
emailFeatureItem("🔥", "Smoke Detector Test", "Monthly", "#FDF3EE", "#2D3436")
bodyContent := fmt.Sprintf(`
%s
Hi %s, great job adding your property! Now set up your first task. Here are some ideas:
%s
%s
Set up recurring tasks and we'll remind you when they're due.
%s
%s`,
emailHeader("Create your first task"),
emailFontStack, name,
emailCalloutBox(features),
emailButton("Create Your First Task", "honeydue://add-task", "#C4856A"),
emailFontStack,
trackingPixel,
emailFooter(time.Now().Year()))
htmlBody := fmt.Sprintf(baseEmailTemplate(), subject, bodyContent)
textBody := fmt.Sprintf(`Create Your First Task
Hi %s,
Great job adding your property! Now set up your first task. Here are some ideas:
- HVAC Filter Replacement: Monthly or quarterly
- Water Heater Flush: Annually
- Lawn Care: Weekly or bi-weekly
- Gutter Cleaning: Seasonal
- Smoke Detector Test: Monthly
Set up recurring tasks and we'll remind you when they're due.
- The honeyDue Team
`, name)
return s.SendEmail(to, subject, htmlBody, textBody)
}
// EmailTemplate represents an email template
type EmailTemplate struct {
name string
template *template.Template
}
// ParseTemplate parses an email template from a string
func ParseTemplate(name, tmpl string) (*EmailTemplate, error) {
t, err := template.New(name).Parse(tmpl)
if err != nil {
return nil, err
}
return &EmailTemplate{name: name, template: t}, nil
}
// Execute executes the template with the given data
func (t *EmailTemplate) Execute(data interface{}) (string, error) {
var buf bytes.Buffer
if err := t.template.Execute(&buf, data); err != nil {
return "", err
}
return buf.String(), nil
}