@@ -154,8 +154,16 @@ func (e *EmailSender) sendOneEmailWithAnalysis(recipient string, reportImage, ma
154154
155155 // Create subject with analysis title
156156 subject := "CleanApp Report"
157+ isDigital := analysis != nil && analysis .Classification == "digital"
158+ if isDigital {
159+ subject = "CleanApp alert: major new issue reported for your brand"
160+ }
157161 if analysis .Title != "" {
158- subject = fmt .Sprintf ("CleanApp Report: %s" , analysis .Title )
162+ if isDigital {
163+ subject = fmt .Sprintf ("CleanApp alert: major new issue — %s" , analysis .Title )
164+ } else {
165+ subject = fmt .Sprintf ("CleanApp Report: %s" , analysis .Title )
166+ }
159167 }
160168
161169 to := mail .NewEmail (recipient , recipient )
@@ -279,43 +287,63 @@ func (e *EmailSender) getEmailHtml(recipient string, hasReport, hasMap bool) str
279287
280288// getEmailTextWithAnalysis returns the plain text content for emails with analysis data
281289func (e * EmailSender ) getEmailTextWithAnalysis (recipient string , analysis * models.ReportAnalysis , hasReport , hasMap bool ) string {
282- var content string
290+ if analysis .Classification == "digital" {
291+ digitalSubject := "CleanApp alert: major new issue reported for your brand"
292+ preheader := "Someone just submitted a brand-related digital report with photos."
283293
284- attachments := ""
285- if hasReport || hasMap {
286- attachments = "\n This email contains:\n "
294+ heroReport := ""
287295 if hasReport {
288- attachments + = "- The report image \n "
296+ heroReport = "\n - Hero: photo of report included. "
289297 }
298+
299+ heroLocation := ""
290300 if hasMap {
291- attachments + = "- A map showing the location\n "
301+ heroLocation = "\n - Hero: photo of location included. "
292302 }
293- attachments += "- AI analysis results\n "
294- }
295- if analysis .Classification == "digital" {
296- content = fmt .Sprintf (`Hello,
297303
298- You have received a new CleanApp digital issue report with analysis.
304+ return fmt .Sprintf (`%s
305+ Preheader: %s
299306
300- REPORT ANALYSIS:
301- Title: %s
302- Description: %s
303- Type: Digital Issue
307+ Someone just submitted a new digital report mentioning your brand.
308+ CleanApp AI analyzed this issue to highlight potential legal and risk ranges connected to your brand presence.%s%s
309+
310+ AI analysis summary:
311+ - Title: %s
312+ - Description: %s
313+ - Type: Digital Issue
314+
315+ Open the Brand Dashboard to see the AI rationale, mapped areas, and supporting media:
304316%s
305- Note: This is a digital issue report. Physical metrics (litter/hazard probability) are not applicable.
306317
307318To unsubscribe from these emails, please visit: %s?email=%s
308319You can also reply to this email with "UNSUBSCRIBE" in the subject line.
309320
310321Best regards,
311322The CleanApp Team` ,
323+ digitalSubject ,
324+ preheader ,
325+ heroReport ,
326+ heroLocation ,
312327 analysis .Title ,
313328 analysis .Description ,
314- attachments ,
329+ e . config . BrandDashboardURL ,
315330 e .config .OptOutURL ,
316331 recipient )
317- } else {
318- content = fmt .Sprintf (`Hello,
332+ }
333+
334+ attachments := ""
335+ if hasReport || hasMap {
336+ attachments = "\n This email contains:\n "
337+ if hasReport {
338+ attachments += "- The report image\n "
339+ }
340+ if hasMap {
341+ attachments += "- A map showing the location\n "
342+ }
343+ attachments += "- AI analysis results\n "
344+ }
345+
346+ return fmt .Sprintf (`Hello,
319347
320348You have received a new CleanApp report with analysis.
321349
@@ -334,29 +362,116 @@ You can also reply to this email with "UNSUBSCRIBE" in the subject line.
334362
335363Best regards,
336364The CleanApp Team` ,
365+ analysis .Title ,
366+ analysis .Description ,
367+ analysis .LitterProbability * 100 ,
368+ analysis .HazardProbability * 100 ,
369+ analysis .SeverityLevel ,
370+ attachments ,
371+ e .config .OptOutURL ,
372+ recipient )
373+ }
374+
375+ // getEmailHtmlWithAnalysis returns the HTML content for emails with analysis data
376+ func (e * EmailSender ) getEmailHtmlWithAnalysis (recipient string , analysis * models.ReportAnalysis , hasReport , hasMap bool ) string {
377+ isDigital := analysis .Classification == "digital"
378+
379+ if isDigital {
380+ subjectLine := "CleanApp alert: major new issue reported for your brand"
381+ preheader := "Someone just submitted a brand-related digital report. Review the AI analysis and risk ranges."
382+
383+ reportHero := ""
384+ if hasReport {
385+ reportHero = fmt .Sprintf (`
386+ <div class="hero-card">
387+ <div class="hero-label">Photo of report</div>
388+ <img src="cid:%s" alt="Report Image" />
389+ </div>` , reportImgCid )
390+ }
391+
392+ locationHero := ""
393+ if hasMap {
394+ locationHero = fmt .Sprintf (`
395+ <div class="hero-card">
396+ <div class="hero-label">Photo of location</div>
397+ <img src="cid:%s" alt="Location Map" />
398+ </div>` , mapImgCid )
399+ }
400+
401+ heroImages := ""
402+ if reportHero != "" || locationHero != "" {
403+ heroImages = fmt .Sprintf (`
404+ <div class="hero-grid">%s%s
405+ </div>` , reportHero , locationHero )
406+ }
407+
408+ return fmt .Sprintf (`<!DOCTYPE html>
409+ <html>
410+ <head>
411+ <meta charset="utf-8">
412+ <title>%s</title>
413+ <style>
414+ body { font-family: Arial, sans-serif; line-height: 1.6; color: #1f2937; background: #f7f7f8; margin: 0; padding: 0; }
415+ .preheader { display: none; visibility: hidden; opacity: 0; height: 0; width: 0; overflow: hidden; }
416+ .container { max-width: 720px; margin: 0 auto; padding: 24px; background: #ffffff; }
417+ .hero { background: linear-gradient(135deg, #0f766e, #14b8a6); color: #ffffff; padding: 28px; border-radius: 14px; box-shadow: 0 10px 30px rgba(0,0,0,0.12); }
418+ .eyebrow { text-transform: uppercase; letter-spacing: 0.08em; font-weight: 700; font-size: 12px; margin: 0 0 6px 0; opacity: 0.85; }
419+ h1 { margin: 0 0 10px 0; font-size: 26px; }
420+ .subhead { margin: 0 0 12px 0; font-size: 16px; opacity: 0.95; }
421+ .lede { margin: 0 0 18px 0; font-size: 15px; }
422+ .cta { display: inline-block; background: #ffffff; color: #0f172a; padding: 12px 18px; border-radius: 10px; text-decoration: none; font-weight: 700; box-shadow: 0 8px 20px rgba(0,0,0,0.12); }
423+ .card { margin-top: 24px; padding: 18px; border: 1px solid #e5e7eb; border-radius: 12px; background: #f8fafc; }
424+ .card h3 { margin-top: 0; color: #0f172a; }
425+ .card p { margin: 6px 0; }
426+ .card .note { margin-top: 12px; color: #475569; }
427+ .hero-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; margin-top: 18px; }
428+ .hero-card { background: #0b766c0d; border: 1px solid #d1fae5; border-radius: 12px; padding: 12px; text-align: center; }
429+ .hero-label { font-weight: 700; color: #0f766e; margin-bottom: 10px; }
430+ .hero-card img { max-width: 100%%; border-radius: 10px; }
431+ .footer { margin-top: 24px; font-size: 13px; color: #6b7280; text-align: left; }
432+ .footer a { color: #0ea5e9; text-decoration: none; }
433+ </style>
434+ </head>
435+ <body>
436+ <div class="preheader">%s</div>
437+ <div class="container">
438+ <div class="hero">
439+ <p class="eyebrow">CleanApp alert</p>
440+ <h1>Major new issue reported for your brand</h1>
441+ <p class="subhead">Someone just submitted a brand-related digital report.</p>
442+ <p class="lede">CleanApp AI analyzed this issue to highlight potential legal and risk ranges connected to your brand presence.</p>
443+ <a class="cta" href="%s">Open brand dashboard</a>
444+ </div>
445+
446+ <div class="card">
447+ <h3>AI analysis summary</h3>
448+ <p><strong>Title:</strong> %s</p>
449+ <p><strong>Description:</strong> %s</p>
450+ <p><strong>Type:</strong> Digital Issue</p>
451+ <p class="note">Review the dashboard to see the AI rationale, mapped legal/risk ranges, and supporting media.</p>
452+ </div>%s
453+
454+ <div class="footer">
455+ <p>To unsubscribe from these emails, please <a href="%s?email=%s">click here</a>.</p>
456+ </div>
457+ </div>
458+ </body>
459+ </html>` ,
460+ subjectLine ,
461+ preheader ,
462+ e .config .BrandDashboardURL ,
337463 analysis .Title ,
338464 analysis .Description ,
339- analysis .LitterProbability * 100 ,
340- analysis .HazardProbability * 100 ,
341- analysis .SeverityLevel ,
342- attachments ,
465+ heroImages ,
343466 e .config .OptOutURL ,
344467 recipient )
345468 }
346469
347- return content
348- }
349-
350- // getEmailHtmlWithAnalysis returns the HTML content for emails with analysis data
351- func (e * EmailSender ) getEmailHtmlWithAnalysis (recipient string , analysis * models.ReportAnalysis , hasReport , hasMap bool ) string {
352470 // Calculate gauge colors based on values
353471 litterColor := e .getGaugeColor (analysis .LitterProbability )
354472 hazardColor := e .getGaugeColor (analysis .HazardProbability )
355473 severityColor := e .getSeverityGaugeColor (analysis .SeverityLevel )
356474
357- // Determine if this is a digital report
358- isDigital := analysis .Classification == "digital"
359-
360475 imagesSection := ""
361476 if hasReport {
362477 imagesSection += fmt .Sprintf (`
@@ -403,21 +518,21 @@ func (e *EmailSender) getEmailHtmlWithAnalysis(recipient string, analysis *model
403518 <h2>CleanApp Report Analysis</h2>
404519 <p>A new report has been analyzed and requires your attention.</p>
405520 </div>
406-
521+
407522 <div class="analysis-section">
408523 <h3>Report Details</h3>
409524 <p><strong>Title:</strong> %s</p>
410525 <p><strong>Description:</strong> %s</p>
411526 <p><strong>Type:</strong> %s</p>
412527 </div>
413-
528+
414529 %s
415-
530+
416531 <div class="images">%s
417532 </div>
418-
533+
419534 <p><em>Best regards,<br>The CleanApp Team</em></p>
420-
535+
421536 <div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; font-size: 0.9em; color: #666;">
422537 <p>To unsubscribe from these emails, please <a href="%s?email=%s" style="color: #007bff; text-decoration: none;">click here</a></p>
423538 </div>
0 commit comments