Files
descrybe/apps/api/internal/campaigns/templates.go
T
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

120 lines
3.6 KiB
Go

package campaigns
import (
"fmt"
"strings"
)
// TemplateKey is a seasonal or custom campaign template identifier.
type TemplateKey string
const (
TemplateChristmas TemplateKey = "christmas"
TemplateBlackFriday TemplateKey = "black_friday"
TemplateSpring TemplateKey = "spring"
TemplateCustom TemplateKey = "custom"
)
type Template struct {
Key string `json:"key"`
Name string `json:"name"`
Season string `json:"season"`
DefaultSubject string `json:"default_subject"`
DefaultPrompt string `json:"default_prompt"`
Description string `json:"description"`
}
var builtInTemplates = []Template{
{
Key: string(TemplateChristmas),
Name: "Christmas",
Season: "christmas",
DefaultSubject: "Holiday picks from {{brand}}",
DefaultPrompt: "Warm Christmas email for these products. Festive, concise, clear CTA. JSON only.",
Description: "Festive seasonal campaign for holiday shoppers.",
},
{
Key: string(TemplateBlackFriday),
Name: "Black Friday",
Season: "black_friday",
DefaultSubject: "Black Friday deals from {{brand}}",
DefaultPrompt: "Urgent Black Friday email for these products. Limited-time value, no false claims, strong CTA. JSON only.",
Description: "Deal-focused Black Friday / Cyber Week campaign.",
},
{
Key: string(TemplateSpring),
Name: "Spring",
Season: "spring",
DefaultSubject: "Fresh for spring — {{brand}}",
DefaultPrompt: "Light spring email for these products. Renewal + practical benefits, clear CTA. JSON only.",
Description: "Seasonal spring refresh campaign.",
},
{
Key: string(TemplateCustom),
Name: "Custom",
Season: "custom",
DefaultSubject: "News from {{brand}}",
DefaultPrompt: "Clear marketing email for these products. Short subject, scannable body, CTA. JSON only.",
Description: "Blank slate with sensible defaults.",
},
}
func ListTemplates() []Template {
out := make([]Template, len(builtInTemplates))
copy(out, builtInTemplates)
return out
}
func GetTemplate(key string) (Template, error) {
key = strings.TrimSpace(strings.ToLower(key))
if key == "" {
key = string(TemplateCustom)
}
for _, t := range builtInTemplates {
if t.Key == key {
return t, nil
}
}
return Template{}, ErrInvalidTemplate
}
func ValidTemplateKey(key string) bool {
_, err := GetTemplate(key)
return err == nil
}
func renderSubject(tpl Template, brand string) string {
if brand == "" {
brand = "our store"
}
return strings.ReplaceAll(tpl.DefaultSubject, "{{brand}}", brand)
}
func templateHTML(subject, intro, productBlock, ctaURL, logoURL string) string {
if ctaURL == "" {
ctaURL = "#"
}
logoBlock := ""
if strings.TrimSpace(logoURL) != "" {
logoBlock = fmt.Sprintf(
`<p style="margin:0 0 16px"><img src="%s" alt="" width="120" style="max-width:160px;height:auto;border:0" /></p>`,
escapeAttr(logoURL),
)
}
return fmt.Sprintf(`<!DOCTYPE html><html><body style="font-family:Arial,sans-serif;color:#222;line-height:1.5">
%s<h1 style="font-size:22px;margin:0 0 12px">%s</h1>
<p>%s</p>
%s
<p style="margin:24px 0"><a href="%s" style="background:#2d1b4e;color:#fff;padding:10px 16px;text-decoration:none;border-radius:4px">Shop now</a></p>
</body></html>`, logoBlock, escapeHTML(subject), escapeHTML(intro), productBlock, escapeAttr(ctaURL))
}
func escapeHTML(s string) string {
r := strings.NewReplacer("&", "&amp;", "<", "&lt;", ">", "&gt;", `"`, "&quot;")
return r.Replace(s)
}
func escapeAttr(s string) string {
return escapeHTML(s)
}