@ucptools/validator 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/config.d.ts +20 -0
- package/dist/auth/config.d.ts.map +1 -0
- package/dist/auth/config.js +114 -0
- package/dist/auth/config.js.map +1 -0
- package/dist/auth/index.d.ts +5 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +17 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/middleware.d.ts +45 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +170 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/auth/service.d.ts +80 -0
- package/dist/auth/service.d.ts.map +1 -0
- package/dist/auth/service.js +298 -0
- package/dist/auth/service.js.map +1 -0
- package/dist/cli/index.js +96 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mock-server.d.ts +20 -0
- package/dist/cli/mock-server.d.ts.map +1 -0
- package/dist/cli/mock-server.js +261 -0
- package/dist/cli/mock-server.js.map +1 -0
- package/dist/db/index.d.ts +8 -2
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +22 -5
- package/dist/db/index.js.map +1 -1
- package/dist/db/schema.d.ts +3570 -128
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +377 -17
- package/dist/db/schema.js.map +1 -1
- package/dist/db/utils.d.ts +252 -0
- package/dist/db/utils.d.ts.map +1 -0
- package/dist/db/utils.js +295 -0
- package/dist/db/utils.js.map +1 -0
- package/dist/feed-analyzer/feed-analyzer.d.ts.map +1 -1
- package/dist/feed-analyzer/feed-analyzer.js +218 -4
- package/dist/feed-analyzer/feed-analyzer.js.map +1 -1
- package/dist/feed-analyzer/types.d.ts +82 -1
- package/dist/feed-analyzer/types.d.ts.map +1 -1
- package/dist/feed-analyzer/types.js +13 -0
- package/dist/feed-analyzer/types.js.map +1 -1
- package/dist/lib/analytics.d.ts +337 -0
- package/dist/lib/analytics.d.ts.map +1 -0
- package/dist/lib/analytics.js +188 -0
- package/dist/lib/analytics.js.map +1 -0
- package/dist/security/security-scanner.d.ts.map +1 -1
- package/dist/security/security-scanner.js +130 -2
- package/dist/security/security-scanner.js.map +1 -1
- package/dist/security/types.d.ts +32 -0
- package/dist/security/types.d.ts.map +1 -1
- package/dist/security/types.js.map +1 -1
- package/dist/services/analytics.d.ts +114 -0
- package/dist/services/analytics.d.ts.map +1 -0
- package/dist/services/analytics.js +862 -0
- package/dist/services/analytics.js.map +1 -0
- package/dist/services/badge.d.ts +31 -0
- package/dist/services/badge.d.ts.map +1 -0
- package/dist/services/badge.js +152 -0
- package/dist/services/badge.js.map +1 -0
- package/dist/services/cron.d.ts +125 -0
- package/dist/services/cron.d.ts.map +1 -0
- package/dist/services/cron.js +613 -0
- package/dist/services/cron.js.map +1 -0
- package/dist/services/directory.d.ts +2 -0
- package/dist/services/directory.d.ts.map +1 -1
- package/dist/services/directory.js +45 -27
- package/dist/services/directory.js.map +1 -1
- package/dist/services/email.d.ts +112 -0
- package/dist/services/email.d.ts.map +1 -0
- package/dist/services/email.js +772 -0
- package/dist/services/email.js.map +1 -0
- package/dist/services/hosted-profiles.d.ts +77 -0
- package/dist/services/hosted-profiles.d.ts.map +1 -0
- package/dist/services/hosted-profiles.js +433 -0
- package/dist/services/hosted-profiles.js.map +1 -0
- package/dist/services/latency.d.ts +67 -0
- package/dist/services/latency.d.ts.map +1 -0
- package/dist/services/latency.js +274 -0
- package/dist/services/latency.js.map +1 -0
- package/dist/services/manifest-compliance.d.ts +64 -0
- package/dist/services/manifest-compliance.d.ts.map +1 -0
- package/dist/services/manifest-compliance.js +271 -0
- package/dist/services/manifest-compliance.js.map +1 -0
- package/dist/services/monitoring-diff.d.ts +31 -0
- package/dist/services/monitoring-diff.d.ts.map +1 -0
- package/dist/services/monitoring-diff.js +189 -0
- package/dist/services/monitoring-diff.js.map +1 -0
- package/dist/services/notifications.d.ts +46 -0
- package/dist/services/notifications.d.ts.map +1 -0
- package/dist/services/notifications.js +88 -0
- package/dist/services/notifications.js.map +1 -0
- package/dist/services/stripe.d.ts +93 -0
- package/dist/services/stripe.d.ts.map +1 -0
- package/dist/services/stripe.js +490 -0
- package/dist/services/stripe.js.map +1 -0
- package/dist/services/validation-history.d.ts +99 -0
- package/dist/services/validation-history.d.ts.map +1 -0
- package/dist/services/validation-history.js +344 -0
- package/dist/services/validation-history.js.map +1 -0
- package/dist/services/validation-logging.d.ts +103 -0
- package/dist/services/validation-logging.d.ts.map +1 -0
- package/dist/services/validation-logging.js +210 -0
- package/dist/services/validation-logging.js.map +1 -0
- package/dist/services/validation.d.ts +119 -0
- package/dist/services/validation.d.ts.map +1 -0
- package/dist/services/validation.js +1185 -0
- package/dist/services/validation.js.map +1 -0
- package/dist/simulator/agent-simulator.d.ts.map +1 -1
- package/dist/simulator/agent-simulator.js +229 -9
- package/dist/simulator/agent-simulator.js.map +1 -1
- package/dist/simulator/types.d.ts +26 -0
- package/dist/simulator/types.d.ts.map +1 -1
- package/dist/simulator/types.js.map +1 -1
- package/dist/types/acp-validation.d.ts +87 -0
- package/dist/types/acp-validation.d.ts.map +1 -0
- package/dist/types/acp-validation.js +40 -0
- package/dist/types/acp-validation.js.map +1 -0
- package/dist/types/analytics.d.ts +182 -0
- package/dist/types/analytics.d.ts.map +1 -0
- package/dist/types/analytics.js +7 -0
- package/dist/types/analytics.js.map +1 -0
- package/dist/types/ucp-profile.d.ts +10 -2
- package/dist/types/ucp-profile.d.ts.map +1 -1
- package/dist/types/ucp-profile.js.map +1 -1
- package/dist/types/validation.d.ts +8 -0
- package/dist/types/validation.d.ts.map +1 -1
- package/dist/types/validation.js +10 -0
- package/dist/types/validation.js.map +1 -1
- package/dist/validator/acp/index.d.ts +31 -0
- package/dist/validator/acp/index.d.ts.map +1 -0
- package/dist/validator/acp/index.js +574 -0
- package/dist/validator/acp/index.js.map +1 -0
- package/dist/validator/network-validator.d.ts.map +1 -1
- package/dist/validator/network-validator.js +4 -4
- package/dist/validator/network-validator.js.map +1 -1
- package/dist/validator/rules-validator.d.ts +8 -0
- package/dist/validator/rules-validator.d.ts.map +1 -1
- package/dist/validator/rules-validator.js +92 -43
- package/dist/validator/rules-validator.js.map +1 -1
- package/dist/validator/structural-validator.d.ts.map +1 -1
- package/dist/validator/structural-validator.js +187 -53
- package/dist/validator/structural-validator.js.map +1 -1
- package/dist/validator/utils.d.ts +51 -0
- package/dist/validator/utils.d.ts.map +1 -0
- package/dist/validator/utils.js +132 -0
- package/dist/validator/utils.js.map +1 -0
- package/package.json +44 -12
- package/.claude/settings.local.json +0 -60
- package/.vercel/README.txt +0 -11
- package/.vercel/project.json +0 -1
- package/publish-output.txt +0 -0
- package/tsconfig.json +0 -20
|
@@ -0,0 +1,772 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Email Service
|
|
4
|
+
*
|
|
5
|
+
* Handles sending transactional emails via MailerSend API.
|
|
6
|
+
* Used for validation alerts, welcome emails, etc.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.shouldSendAlert = shouldSendAlert;
|
|
10
|
+
exports.sendEmail = sendEmail;
|
|
11
|
+
exports.sendScoreChangeAlert = sendScoreChangeAlert;
|
|
12
|
+
exports.sendSeverityAlert = sendSeverityAlert;
|
|
13
|
+
exports.sendWelcomeEmail = sendWelcomeEmail;
|
|
14
|
+
exports.sendPasswordResetEmail = sendPasswordResetEmail;
|
|
15
|
+
exports.sendTrialDay1Email = sendTrialDay1Email;
|
|
16
|
+
exports.sendTrialDay3Email = sendTrialDay3Email;
|
|
17
|
+
exports.sendTrialDay6Email = sendTrialDay6Email;
|
|
18
|
+
exports.sendTrialExpiredEmail = sendTrialExpiredEmail;
|
|
19
|
+
const MAILERSEND_API_URL = 'https://api.mailersend.com/v1/email';
|
|
20
|
+
/**
|
|
21
|
+
* Determine if an alert email should be sent based on score change
|
|
22
|
+
* Threshold: ±10 points
|
|
23
|
+
*/
|
|
24
|
+
function shouldSendAlert(oldScore, newScore) {
|
|
25
|
+
if (oldScore === null || newScore === null) {
|
|
26
|
+
return false; // Don't send alert for first validation or missing data
|
|
27
|
+
}
|
|
28
|
+
const change = Math.abs(newScore - oldScore);
|
|
29
|
+
return change >= 10;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Send an email via MailerSend
|
|
33
|
+
*/
|
|
34
|
+
async function sendEmail(options) {
|
|
35
|
+
const apiKey = process.env.MAILERSEND_API_KEY;
|
|
36
|
+
if (!apiKey) {
|
|
37
|
+
console.error('[Email] MAILERSEND_API_KEY not configured');
|
|
38
|
+
return { success: false, error: 'Email service not configured' };
|
|
39
|
+
}
|
|
40
|
+
const fromEmail = options.from || {
|
|
41
|
+
email: process.env.MAILERSEND_FROM_EMAIL || 'alerts@ucptools.dev',
|
|
42
|
+
name: process.env.MAILERSEND_FROM_NAME || 'UCP Tools',
|
|
43
|
+
};
|
|
44
|
+
try {
|
|
45
|
+
const response = await fetch(MAILERSEND_API_URL, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: {
|
|
48
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
49
|
+
'Content-Type': 'application/json',
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify({
|
|
52
|
+
from: fromEmail,
|
|
53
|
+
to: options.to,
|
|
54
|
+
subject: options.subject,
|
|
55
|
+
text: options.text,
|
|
56
|
+
html: options.html,
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
if (response.ok) {
|
|
60
|
+
const messageId = response.headers.get('x-message-id') || undefined;
|
|
61
|
+
console.log(`[Email] Sent successfully to ${options.to.map(r => r.email).join(', ')}`);
|
|
62
|
+
return { success: true, messageId };
|
|
63
|
+
}
|
|
64
|
+
// Handle errors
|
|
65
|
+
const errorData = await response.json().catch(() => ({}));
|
|
66
|
+
const errorMessage = errorData.message || `HTTP ${response.status}`;
|
|
67
|
+
console.error(`[Email] Failed to send: ${errorMessage}`);
|
|
68
|
+
return { success: false, error: errorMessage };
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
72
|
+
console.error('[Email] Send error:', errorMessage);
|
|
73
|
+
return { success: false, error: errorMessage };
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function getGradeColor(grade) {
|
|
77
|
+
switch (grade) {
|
|
78
|
+
case 'A': return '#22c55e';
|
|
79
|
+
case 'B': return '#84cc16';
|
|
80
|
+
case 'C': return '#eab308';
|
|
81
|
+
case 'D': return '#f97316';
|
|
82
|
+
case 'F': return '#ef4444';
|
|
83
|
+
default: return '#6b7280';
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Branded email header with logo and gradient title bar
|
|
88
|
+
*/
|
|
89
|
+
function getEmailHeader(title, gradient = '#2E86AB 0%, #36B5A2 100%') {
|
|
90
|
+
return `<div style="background: linear-gradient(135deg, ${gradient}); padding: 24px 30px 20px; border-radius: 12px 12px 0 0; text-align: center;">
|
|
91
|
+
<img src="https://ucptools.dev/og-image-icon.png" alt="UCP.tools" width="48" height="48" style="margin-bottom: 12px; border-radius: 10px;" />
|
|
92
|
+
<h1 style="color: white; margin: 0; font-size: 24px;">${title}</h1>
|
|
93
|
+
</div>`;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Branded email footer with social links and email preferences
|
|
97
|
+
*/
|
|
98
|
+
function getEmailFooter() {
|
|
99
|
+
return `<hr style="border: none; border-top: 1px solid #e9ecef; margin: 30px 0;">
|
|
100
|
+
<div style="text-align: center;">
|
|
101
|
+
<p style="margin: 0 0 12px 0;">
|
|
102
|
+
<a href="https://www.linkedin.com/company/ucptools" style="color: #2E86AB; text-decoration: none; font-size: 13px; margin: 0 6px;">LinkedIn</a>
|
|
103
|
+
<span style="color: #ccc;">·</span>
|
|
104
|
+
<a href="https://x.com/ucptoolsdev" style="color: #2E86AB; text-decoration: none; font-size: 13px; margin: 0 6px;">X / Twitter</a>
|
|
105
|
+
<span style="color: #ccc;">·</span>
|
|
106
|
+
<a href="https://ucptools.dev" style="color: #2E86AB; text-decoration: none; font-size: 13px; margin: 0 6px;">ucptools.dev</a>
|
|
107
|
+
</p>
|
|
108
|
+
<p style="font-size: 12px; color: #999; margin: 0;">
|
|
109
|
+
UCP Tools — AI Commerce Readiness Platform<br>
|
|
110
|
+
Questions? Reply to this email — we read everything.
|
|
111
|
+
</p>
|
|
112
|
+
<p style="font-size: 11px; color: #bbb; margin: 12px 0 0 0;">
|
|
113
|
+
<a href="https://ucptools.dev/dashboard/settings" style="color: #bbb;">Email preferences</a>
|
|
114
|
+
</p>
|
|
115
|
+
</div>`;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Send a score change alert email
|
|
119
|
+
*/
|
|
120
|
+
async function sendScoreChangeAlert(params) {
|
|
121
|
+
const { to, domain, oldScore, newScore, oldGrade, newGrade, dashboardUrl } = params;
|
|
122
|
+
const scoreChange = newScore - oldScore;
|
|
123
|
+
const direction = scoreChange > 0 ? 'improved' : 'declined';
|
|
124
|
+
const changeText = scoreChange > 0 ? `+${scoreChange}` : `${scoreChange}`;
|
|
125
|
+
const emoji = scoreChange > 0 ? '📈' : '📉';
|
|
126
|
+
const subject = `${emoji} ${domain}: Score ${direction} (${changeText} points)`;
|
|
127
|
+
const html = `
|
|
128
|
+
<!DOCTYPE html>
|
|
129
|
+
<html>
|
|
130
|
+
<head>
|
|
131
|
+
<meta charset="utf-8">
|
|
132
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
133
|
+
<title>UCP Score Alert</title>
|
|
134
|
+
</head>
|
|
135
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
136
|
+
${getEmailHeader('UCP Score Alert', '#2E86AB 0%, #1a5c7a 100%')}
|
|
137
|
+
|
|
138
|
+
<div style="background: #f8f9fa; padding: 30px; border-radius: 0 0 12px 12px; border: 1px solid #e9ecef; border-top: none;">
|
|
139
|
+
<p style="margin-top: 0;">Hi${to.name ? ` ${to.name}` : ''},</p>
|
|
140
|
+
|
|
141
|
+
<p>Your UCP readiness score for <strong>${domain}</strong> has ${direction}:</p>
|
|
142
|
+
|
|
143
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
144
|
+
<div style="display: inline-block; margin: 0 20px;">
|
|
145
|
+
<div style="font-size: 14px; color: #666; margin-bottom: 4px;">Previous</div>
|
|
146
|
+
<div style="font-size: 32px; font-weight: bold; color: #666;">${oldScore}</div>
|
|
147
|
+
<div style="font-size: 18px; font-weight: bold; color: ${getGradeColor(oldGrade)};">Grade ${oldGrade}</div>
|
|
148
|
+
</div>
|
|
149
|
+
<div style="display: inline-block; font-size: 24px; color: #999; margin: 0 10px;">→</div>
|
|
150
|
+
<div style="display: inline-block; margin: 0 20px;">
|
|
151
|
+
<div style="font-size: 14px; color: #666; margin-bottom: 4px;">Current</div>
|
|
152
|
+
<div style="font-size: 32px; font-weight: bold; color: ${scoreChange > 0 ? '#22c55e' : '#ef4444'};">${newScore}</div>
|
|
153
|
+
<div style="font-size: 18px; font-weight: bold; color: ${getGradeColor(newGrade)};">Grade ${newGrade}</div>
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<p style="text-align: center;">
|
|
158
|
+
<span style="display: inline-block; padding: 8px 16px; border-radius: 20px; font-weight: 600; background: ${scoreChange > 0 ? '#dcfce7' : '#fee2e2'}; color: ${scoreChange > 0 ? '#166534' : '#991b1b'};">
|
|
159
|
+
${emoji} ${changeText} points
|
|
160
|
+
</span>
|
|
161
|
+
</p>
|
|
162
|
+
|
|
163
|
+
<p style="text-align: center; margin: 30px 0;">
|
|
164
|
+
<a href="${dashboardUrl}" style="display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #2E86AB 0%, #1a5c7a 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">View Full Report</a>
|
|
165
|
+
</p>
|
|
166
|
+
|
|
167
|
+
${getEmailFooter()}
|
|
168
|
+
</div>
|
|
169
|
+
</body>
|
|
170
|
+
</html>
|
|
171
|
+
`;
|
|
172
|
+
const text = `
|
|
173
|
+
UCP Score Alert
|
|
174
|
+
|
|
175
|
+
Hi${to.name ? ` ${to.name}` : ''},
|
|
176
|
+
|
|
177
|
+
Your UCP readiness score for ${domain} has ${direction}:
|
|
178
|
+
|
|
179
|
+
Previous: ${oldScore} (Grade ${oldGrade})
|
|
180
|
+
Current: ${newScore} (Grade ${newGrade})
|
|
181
|
+
Change: ${changeText} points
|
|
182
|
+
|
|
183
|
+
View your full report: ${dashboardUrl}
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
UCP Tools - AI Commerce Readiness Platform
|
|
187
|
+
Questions? Reply to this email - we read everything.
|
|
188
|
+
`;
|
|
189
|
+
return sendEmail({
|
|
190
|
+
to: [to],
|
|
191
|
+
subject,
|
|
192
|
+
html,
|
|
193
|
+
text,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Send a severity-aware alert email with richer context.
|
|
198
|
+
* Uses severity to determine header color, subject line, and content.
|
|
199
|
+
*/
|
|
200
|
+
async function sendSeverityAlert(params) {
|
|
201
|
+
const { to, domain, severity, oldScore, newScore, oldGrade, newGrade, summary, changes, dashboardUrl } = params;
|
|
202
|
+
const config = {
|
|
203
|
+
broken: {
|
|
204
|
+
emoji: '🚨',
|
|
205
|
+
label: 'ACTION REQUIRED — UCP Profile Broken',
|
|
206
|
+
gradient: '#dc2626 0%, #991b1b 100%',
|
|
207
|
+
accentBg: '#fee2e2',
|
|
208
|
+
accentText: '#991b1b',
|
|
209
|
+
},
|
|
210
|
+
degraded: {
|
|
211
|
+
emoji: '⚠️',
|
|
212
|
+
label: 'UCP Profile Degraded',
|
|
213
|
+
gradient: '#ea580c 0%, #c2410c 100%',
|
|
214
|
+
accentBg: '#ffedd5',
|
|
215
|
+
accentText: '#9a3412',
|
|
216
|
+
},
|
|
217
|
+
changed: {
|
|
218
|
+
emoji: '📊',
|
|
219
|
+
label: 'UCP Profile Updated',
|
|
220
|
+
gradient: '#2563eb 0%, #1d4ed8 100%',
|
|
221
|
+
accentBg: '#dbeafe',
|
|
222
|
+
accentText: '#1e40af',
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
const c = config[severity] || config.changed;
|
|
226
|
+
const subject = `${c.emoji} ${domain}: ${c.label}`;
|
|
227
|
+
const scoreSection = oldScore !== null ? `
|
|
228
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
229
|
+
<div style="display: inline-block; margin: 0 20px;">
|
|
230
|
+
<div style="font-size: 14px; color: #666; margin-bottom: 4px;">Previous</div>
|
|
231
|
+
<div style="font-size: 32px; font-weight: bold; color: #666;">${oldScore}</div>
|
|
232
|
+
<div style="font-size: 18px; font-weight: bold; color: ${getGradeColor(oldGrade || 'F')};">Grade ${oldGrade || '?'}</div>
|
|
233
|
+
</div>
|
|
234
|
+
<div style="display: inline-block; font-size: 24px; color: #999; margin: 0 10px;">→</div>
|
|
235
|
+
<div style="display: inline-block; margin: 0 20px;">
|
|
236
|
+
<div style="font-size: 14px; color: #666; margin-bottom: 4px;">Current</div>
|
|
237
|
+
<div style="font-size: 32px; font-weight: bold; color: ${getGradeColor(newGrade)};">${newScore}</div>
|
|
238
|
+
<div style="font-size: 18px; font-weight: bold; color: ${getGradeColor(newGrade)};">Grade ${newGrade}</div>
|
|
239
|
+
</div>
|
|
240
|
+
</div>` : '';
|
|
241
|
+
const changesSection = changes.length > 0 ? `
|
|
242
|
+
<div style="background: white; border-radius: 8px; padding: 16px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
243
|
+
<h3 style="margin: 0 0 12px 0; font-size: 15px; color: #374151;">What changed</h3>
|
|
244
|
+
${changes.slice(0, 10).map(ch => `
|
|
245
|
+
<div style="padding: 6px 0; border-bottom: 1px solid #f3f4f6; font-size: 14px;">
|
|
246
|
+
<strong style="color: ${ch.type === 'removed' ? '#dc2626' : ch.type === 'added' ? '#16a34a' : '#2563eb'};">
|
|
247
|
+
${ch.type === 'removed' ? '−' : ch.type === 'added' ? '+' : '~'}
|
|
248
|
+
</strong>
|
|
249
|
+
${ch.field}${ch.oldValue !== undefined && ch.newValue !== undefined ? `: ${ch.oldValue} → ${ch.newValue}` : ''}
|
|
250
|
+
</div>
|
|
251
|
+
`).join('')}
|
|
252
|
+
${changes.length > 10 ? `<div style="padding: 8px 0; font-size: 13px; color: #6b7280;">...and ${changes.length - 10} more changes</div>` : ''}
|
|
253
|
+
</div>` : '';
|
|
254
|
+
const html = `
|
|
255
|
+
<!DOCTYPE html>
|
|
256
|
+
<html>
|
|
257
|
+
<head>
|
|
258
|
+
<meta charset="utf-8">
|
|
259
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
260
|
+
<title>${c.label}</title>
|
|
261
|
+
</head>
|
|
262
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
263
|
+
${getEmailHeader(c.label, c.gradient)}
|
|
264
|
+
|
|
265
|
+
<div style="background: #f8f9fa; padding: 30px; border-radius: 0 0 12px 12px; border: 1px solid #e9ecef; border-top: none;">
|
|
266
|
+
<p style="margin-top: 0;">Hi${to.name ? ` ${to.name}` : ''},</p>
|
|
267
|
+
|
|
268
|
+
<div style="background: ${c.accentBg}; color: ${c.accentText}; padding: 12px 16px; border-radius: 8px; margin: 16px 0; font-weight: 500;">
|
|
269
|
+
${summary}
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
${scoreSection}
|
|
273
|
+
${changesSection}
|
|
274
|
+
|
|
275
|
+
<p style="text-align: center; margin: 30px 0;">
|
|
276
|
+
<a href="${dashboardUrl}" style="display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, ${c.gradient}); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">View Full Report</a>
|
|
277
|
+
</p>
|
|
278
|
+
|
|
279
|
+
${getEmailFooter()}
|
|
280
|
+
</div>
|
|
281
|
+
</body>
|
|
282
|
+
</html>
|
|
283
|
+
`;
|
|
284
|
+
const text = `${c.label}
|
|
285
|
+
|
|
286
|
+
Hi${to.name ? ` ${to.name}` : ''},
|
|
287
|
+
|
|
288
|
+
${summary}
|
|
289
|
+
|
|
290
|
+
${oldScore !== null ? `Score: ${oldScore} (${oldGrade}) → ${newScore} (${newGrade})` : `Score: ${newScore} (${newGrade})`}
|
|
291
|
+
|
|
292
|
+
${changes.length > 0 ? 'Changes:\n' + changes.slice(0, 10).map(ch => ` ${ch.type === 'removed' ? '-' : ch.type === 'added' ? '+' : '~'} ${ch.field}`).join('\n') : ''}
|
|
293
|
+
|
|
294
|
+
View full report: ${dashboardUrl}
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
UCP Tools - AI Commerce Readiness Platform
|
|
298
|
+
`;
|
|
299
|
+
return sendEmail({
|
|
300
|
+
to: [to],
|
|
301
|
+
subject,
|
|
302
|
+
html,
|
|
303
|
+
text,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Send a welcome email to new subscribers
|
|
308
|
+
*/
|
|
309
|
+
async function sendWelcomeEmail(params) {
|
|
310
|
+
const { to, dashboardUrl, trialEndsAt } = params;
|
|
311
|
+
const trialEndDate = trialEndsAt
|
|
312
|
+
? new Date(trialEndsAt).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' })
|
|
313
|
+
: null;
|
|
314
|
+
const subject = 'Welcome to UCP Tools — your 7-day free trial starts now!';
|
|
315
|
+
const html = `
|
|
316
|
+
<!DOCTYPE html>
|
|
317
|
+
<html>
|
|
318
|
+
<head>
|
|
319
|
+
<meta charset="utf-8">
|
|
320
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
321
|
+
</head>
|
|
322
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
323
|
+
${getEmailHeader('Your 7-Day Free Trial Has Started!')}
|
|
324
|
+
|
|
325
|
+
<div style="background: #f8f9fa; padding: 30px; border-radius: 0 0 12px 12px; border: 1px solid #e9ecef; border-top: none;">
|
|
326
|
+
<p style="margin-top: 0;">Hi${to.name ? ` ${to.name}` : ''},</p>
|
|
327
|
+
|
|
328
|
+
<p>Welcome to UCP Tools! Your free trial is active${trialEndDate ? ` until <strong>${trialEndDate}</strong>` : ' for the next 7 days'}. No credit card required.</p>
|
|
329
|
+
|
|
330
|
+
<p>Here's what you get during your trial:</p>
|
|
331
|
+
|
|
332
|
+
<ul style="padding-left: 20px;">
|
|
333
|
+
<li><strong>Domain Monitoring</strong> — Track your AI commerce readiness over time</li>
|
|
334
|
+
<li><strong>Weekly Auto-Validation</strong> — We'll check your domain every week</li>
|
|
335
|
+
<li><strong>AI Agent Analytics</strong> — See which AI agents visit your store</li>
|
|
336
|
+
<li><strong>Score Change Alerts</strong> — Get notified when your score changes</li>
|
|
337
|
+
</ul>
|
|
338
|
+
|
|
339
|
+
<div style="background: #f0f9ff; border: 1px solid #bae6fd; border-radius: 8px; padding: 16px; margin: 20px 0;">
|
|
340
|
+
<p style="margin: 0; font-size: 14px; color: #0369a1;"><strong>Over the next 7 days, we'll send you:</strong></p>
|
|
341
|
+
<ul style="margin: 8px 0 0 0; padding-left: 20px; font-size: 14px; color: #0369a1;">
|
|
342
|
+
<li>Day 1: Quick start guide — 3 things to try in 5 minutes</li>
|
|
343
|
+
<li>Day 3: Feature deep-dive — AI analytics, simulator, security</li>
|
|
344
|
+
<li>Day 6: Trial wrap-up — what you've achieved</li>
|
|
345
|
+
</ul>
|
|
346
|
+
</div>
|
|
347
|
+
|
|
348
|
+
<p style="text-align: center; margin: 30px 0;">
|
|
349
|
+
<a href="${dashboardUrl}" style="display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #2E86AB 0%, #36B5A2 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">Go to Dashboard</a>
|
|
350
|
+
</p>
|
|
351
|
+
|
|
352
|
+
${getEmailFooter()}
|
|
353
|
+
</div>
|
|
354
|
+
</body>
|
|
355
|
+
</html>
|
|
356
|
+
`;
|
|
357
|
+
const text = `
|
|
358
|
+
Your 7-Day Free Trial Has Started!
|
|
359
|
+
|
|
360
|
+
Hi${to.name ? ` ${to.name}` : ''},
|
|
361
|
+
|
|
362
|
+
Welcome to UCP Tools! Your free trial is active${trialEndDate ? ` until ${trialEndDate}` : ' for the next 7 days'}. No credit card required.
|
|
363
|
+
|
|
364
|
+
Here's what you get during your trial:
|
|
365
|
+
|
|
366
|
+
- Domain Monitoring - Track your AI commerce readiness over time
|
|
367
|
+
- Weekly Auto-Validation - We'll check your domain every week
|
|
368
|
+
- AI Agent Analytics - See which AI agents visit your store
|
|
369
|
+
- Score Change Alerts - Get notified when your score changes
|
|
370
|
+
|
|
371
|
+
Over the next 7 days, we'll send you:
|
|
372
|
+
- Day 1: Quick start guide - 3 things to try in 5 minutes
|
|
373
|
+
- Day 3: Feature deep-dive - AI analytics, simulator, security
|
|
374
|
+
- Day 6: Trial wrap-up - what you've achieved
|
|
375
|
+
|
|
376
|
+
Go to your dashboard: ${dashboardUrl}
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
UCP Tools - AI Commerce Readiness Platform
|
|
380
|
+
Questions? Reply to this email - we read everything.
|
|
381
|
+
`;
|
|
382
|
+
return sendEmail({
|
|
383
|
+
to: [to],
|
|
384
|
+
subject,
|
|
385
|
+
html,
|
|
386
|
+
text,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Send a password reset email
|
|
391
|
+
*/
|
|
392
|
+
async function sendPasswordResetEmail(params) {
|
|
393
|
+
const { to, resetUrl } = params;
|
|
394
|
+
const subject = 'Reset your UCP Tools password';
|
|
395
|
+
const html = `
|
|
396
|
+
<!DOCTYPE html>
|
|
397
|
+
<html>
|
|
398
|
+
<head>
|
|
399
|
+
<meta charset="utf-8">
|
|
400
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
401
|
+
<title>Password Reset</title>
|
|
402
|
+
</head>
|
|
403
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
404
|
+
${getEmailHeader('Password Reset', '#2E86AB 0%, #1a5c7a 100%')}
|
|
405
|
+
|
|
406
|
+
<div style="background: #f8f9fa; padding: 30px; border-radius: 0 0 12px 12px; border: 1px solid #e9ecef; border-top: none;">
|
|
407
|
+
<p style="margin-top: 0;">Hi${to.name ? ` ${to.name}` : ''},</p>
|
|
408
|
+
|
|
409
|
+
<p>We received a request to reset your password for your UCP Tools account.</p>
|
|
410
|
+
|
|
411
|
+
<p style="text-align: center; margin: 30px 0;">
|
|
412
|
+
<a href="${resetUrl}" style="display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #2E86AB 0%, #1a5c7a 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">Reset Password</a>
|
|
413
|
+
</p>
|
|
414
|
+
|
|
415
|
+
<p>This link will expire in <strong>1 hour</strong>.</p>
|
|
416
|
+
|
|
417
|
+
<p>If you didn't request this password reset, you can safely ignore this email. Your password will remain unchanged.</p>
|
|
418
|
+
|
|
419
|
+
${getEmailFooter()}
|
|
420
|
+
|
|
421
|
+
<p style="font-size: 12px; color: #666; margin-top: 16px;">
|
|
422
|
+
If the button doesn't work, copy and paste this link into your browser:<br>
|
|
423
|
+
<a href="${resetUrl}" style="color: #2E86AB; word-break: break-all;">${resetUrl}</a>
|
|
424
|
+
</p>
|
|
425
|
+
</div>
|
|
426
|
+
</body>
|
|
427
|
+
</html>
|
|
428
|
+
`;
|
|
429
|
+
const text = `
|
|
430
|
+
Password Reset
|
|
431
|
+
|
|
432
|
+
Hi${to.name ? ` ${to.name}` : ''},
|
|
433
|
+
|
|
434
|
+
We received a request to reset your password for your UCP Tools account.
|
|
435
|
+
|
|
436
|
+
Reset your password by visiting this link:
|
|
437
|
+
${resetUrl}
|
|
438
|
+
|
|
439
|
+
This link will expire in 1 hour.
|
|
440
|
+
|
|
441
|
+
If you didn't request this password reset, you can safely ignore this email. Your password will remain unchanged.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
UCP Tools - AI Commerce Readiness Platform
|
|
445
|
+
`;
|
|
446
|
+
return sendEmail({
|
|
447
|
+
to: [to],
|
|
448
|
+
subject,
|
|
449
|
+
html,
|
|
450
|
+
text,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Send trial Day 1 email - Quick Start Guide
|
|
455
|
+
*/
|
|
456
|
+
async function sendTrialDay1Email(params) {
|
|
457
|
+
const { to, dashboardUrl } = params;
|
|
458
|
+
const subject = 'Quick start: 3 things to try in 5 minutes';
|
|
459
|
+
const html = `
|
|
460
|
+
<!DOCTYPE html>
|
|
461
|
+
<html>
|
|
462
|
+
<head>
|
|
463
|
+
<meta charset="utf-8">
|
|
464
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
465
|
+
</head>
|
|
466
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
467
|
+
${getEmailHeader('Your Quick Start Guide')}
|
|
468
|
+
|
|
469
|
+
<div style="background: #f8f9fa; padding: 30px; border-radius: 0 0 12px 12px; border: 1px solid #e9ecef; border-top: none;">
|
|
470
|
+
<p style="margin-top: 0;">Hi${to.name ? ` ${to.name}` : ''},</p>
|
|
471
|
+
|
|
472
|
+
<p>Here are 3 things you can do right now to get the most from your trial:</p>
|
|
473
|
+
|
|
474
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
475
|
+
<h3 style="margin-top: 0; color: #2E86AB;">1. Run your first AI Commerce check</h3>
|
|
476
|
+
<p style="margin-bottom: 0;">Enter your store URL on our <a href="https://ucptools.dev" style="color: #2E86AB;">homepage</a> and get your AI Commerce Score in 30 seconds. See exactly how AI shopping agents like ChatGPT and Gemini experience your store.</p>
|
|
477
|
+
</div>
|
|
478
|
+
|
|
479
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
480
|
+
<h3 style="margin-top: 0; color: #36B5A2;">2. Check your score breakdown</h3>
|
|
481
|
+
<p style="margin-bottom: 0;">Your score is split across UCP Profile (50pts), Schema.org markup (35pts), and Performance (15pts). Each section shows exactly what to fix to improve.</p>
|
|
482
|
+
</div>
|
|
483
|
+
|
|
484
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
485
|
+
<h3 style="margin-top: 0; color: #2E86AB;">3. Try the Agent Simulator</h3>
|
|
486
|
+
<p style="margin-bottom: 0;">See your store through an AI agent's eyes. Our simulator tests discovery, browsing, and checkout — just like a real AI shopping assistant would.</p>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<p style="text-align: center; margin: 30px 0;">
|
|
490
|
+
<a href="https://ucptools.dev" style="display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #2E86AB 0%, #36B5A2 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">Run Your First Check</a>
|
|
491
|
+
</p>
|
|
492
|
+
|
|
493
|
+
<p style="text-align: center; font-size: 13px; color: #666; margin-bottom: 20px;">
|
|
494
|
+
You have <strong>6 days</strong> left in your free trial.
|
|
495
|
+
<a href="${dashboardUrl}/subscription" style="color: #2E86AB;">Manage subscription</a>
|
|
496
|
+
</p>
|
|
497
|
+
|
|
498
|
+
${getEmailFooter()}
|
|
499
|
+
</div>
|
|
500
|
+
</body>
|
|
501
|
+
</html>
|
|
502
|
+
`;
|
|
503
|
+
const text = `
|
|
504
|
+
Your Quick Start Guide
|
|
505
|
+
|
|
506
|
+
Hi${to.name ? ` ${to.name}` : ''},
|
|
507
|
+
|
|
508
|
+
Here are 3 things you can do right now to get the most from your trial:
|
|
509
|
+
|
|
510
|
+
1. RUN YOUR FIRST AI COMMERCE CHECK
|
|
511
|
+
Enter your store URL at https://ucptools.dev and get your AI Commerce Score in 30 seconds.
|
|
512
|
+
|
|
513
|
+
2. CHECK YOUR SCORE BREAKDOWN
|
|
514
|
+
Your score is split across UCP Profile (50pts), Schema.org markup (35pts), and Performance (15pts).
|
|
515
|
+
|
|
516
|
+
3. TRY THE AGENT SIMULATOR
|
|
517
|
+
See your store through an AI agent's eyes. Tests discovery, browsing, and checkout.
|
|
518
|
+
|
|
519
|
+
Run your first check: https://ucptools.dev
|
|
520
|
+
|
|
521
|
+
---
|
|
522
|
+
You have 6 days left in your free trial.
|
|
523
|
+
UCP Tools - AI Commerce Readiness Platform
|
|
524
|
+
Questions? Reply to this email - we read everything.
|
|
525
|
+
`;
|
|
526
|
+
return sendEmail({
|
|
527
|
+
to: [to],
|
|
528
|
+
subject,
|
|
529
|
+
html,
|
|
530
|
+
text,
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Send trial Day 3 email - Value reminder + Feature highlight
|
|
535
|
+
*/
|
|
536
|
+
async function sendTrialDay3Email(params) {
|
|
537
|
+
const { to, dashboardUrl, domainsAdded = 0 } = params;
|
|
538
|
+
const subject = 'Your AI readiness journey is off to a great start 🚀';
|
|
539
|
+
const html = `
|
|
540
|
+
<!DOCTYPE html>
|
|
541
|
+
<html>
|
|
542
|
+
<head>
|
|
543
|
+
<meta charset="utf-8">
|
|
544
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
545
|
+
</head>
|
|
546
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
547
|
+
${getEmailHeader('Day 3 of Your Trial', '#2E86AB 0%, #47C97A 100%')}
|
|
548
|
+
|
|
549
|
+
<div style="background: #f8f9fa; padding: 30px; border-radius: 0 0 12px 12px; border: 1px solid #e9ecef; border-top: none;">
|
|
550
|
+
<p style="margin-top: 0;">Hi${to.name ? ` ${to.name}` : ''},</p>
|
|
551
|
+
|
|
552
|
+
${domainsAdded > 0 ? `
|
|
553
|
+
<p>Great progress! You've already added <strong>${domainsAdded} domain${domainsAdded > 1 ? 's' : ''}</strong> to monitor. Here's what else you can do:</p>
|
|
554
|
+
` : `
|
|
555
|
+
<p>You're 3 days into your trial! Here's what you might be missing:</p>
|
|
556
|
+
`}
|
|
557
|
+
|
|
558
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
559
|
+
<h3 style="margin-top: 0; color: #2E86AB;">📊 AI Agent Analytics</h3>
|
|
560
|
+
<p style="margin-bottom: 0;">See which AI agents visit your store, what they're looking for, and whether they can complete purchases. It's like Google Analytics for the agentic commerce era.</p>
|
|
561
|
+
</div>
|
|
562
|
+
|
|
563
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
564
|
+
<h3 style="margin-top: 0; color: #47C97A;">🤖 Agent Simulator</h3>
|
|
565
|
+
<p style="margin-bottom: 0;">Test how AI shopping agents experience your store. Find issues before real customers do.</p>
|
|
566
|
+
</div>
|
|
567
|
+
|
|
568
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
569
|
+
<h3 style="margin-top: 0; color: #2E86AB;">🔒 Security Scanning</h3>
|
|
570
|
+
<p style="margin-bottom: 0;">Ensure your UCP implementation is secure and follows best practices.</p>
|
|
571
|
+
</div>
|
|
572
|
+
|
|
573
|
+
<p style="text-align: center; margin: 30px 0;">
|
|
574
|
+
<a href="${dashboardUrl}" style="display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #2E86AB 0%, #47C97A 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">Explore Your Dashboard</a>
|
|
575
|
+
</p>
|
|
576
|
+
|
|
577
|
+
<p style="text-align: center; font-size: 13px; color: #666; margin-bottom: 20px;">
|
|
578
|
+
You have <strong>4 days</strong> left in your trial.
|
|
579
|
+
<a href="https://ucptools.dev/dashboard/subscription" style="color: #2E86AB;">Manage subscription</a>
|
|
580
|
+
</p>
|
|
581
|
+
|
|
582
|
+
${getEmailFooter()}
|
|
583
|
+
</div>
|
|
584
|
+
</body>
|
|
585
|
+
</html>
|
|
586
|
+
`;
|
|
587
|
+
const text = `
|
|
588
|
+
Day 3 of Your Trial
|
|
589
|
+
|
|
590
|
+
Hi${to.name ? ` ${to.name}` : ''},
|
|
591
|
+
|
|
592
|
+
${domainsAdded > 0
|
|
593
|
+
? `Great progress! You've already added ${domainsAdded} domain${domainsAdded > 1 ? 's' : ''} to monitor.`
|
|
594
|
+
: `You're 3 days into your trial! Here's what you might be missing:`}
|
|
595
|
+
|
|
596
|
+
FEATURES TO EXPLORE:
|
|
597
|
+
|
|
598
|
+
AI Agent Analytics
|
|
599
|
+
See which AI agents visit your store, what they're looking for, and whether they can complete purchases.
|
|
600
|
+
|
|
601
|
+
Agent Simulator
|
|
602
|
+
Test how AI shopping agents experience your store. Find issues before real customers do.
|
|
603
|
+
|
|
604
|
+
Security Scanning
|
|
605
|
+
Ensure your UCP implementation is secure and follows best practices.
|
|
606
|
+
|
|
607
|
+
Explore your dashboard: ${dashboardUrl}
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
You have 4 days left in your trial.
|
|
611
|
+
UCP Tools - AI Commerce Readiness Platform
|
|
612
|
+
Questions? Reply to this email - we read everything.
|
|
613
|
+
`;
|
|
614
|
+
return sendEmail({
|
|
615
|
+
to: [to],
|
|
616
|
+
subject,
|
|
617
|
+
html,
|
|
618
|
+
text,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Send trial Day 6 email - Trial ending + Conversion CTA
|
|
623
|
+
*/
|
|
624
|
+
async function sendTrialDay6Email(params) {
|
|
625
|
+
const { to, dashboardUrl, domainsAdded = 0, validationsRun = 0 } = params;
|
|
626
|
+
const subject = 'Your trial ends tomorrow – here\'s what you\'ll lose ⏰';
|
|
627
|
+
const html = `
|
|
628
|
+
<!DOCTYPE html>
|
|
629
|
+
<html>
|
|
630
|
+
<head>
|
|
631
|
+
<meta charset="utf-8">
|
|
632
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
633
|
+
</head>
|
|
634
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
635
|
+
${getEmailHeader('⏰ Your Trial Ends Tomorrow', '#f97316 0%, #dc2626 100%')}
|
|
636
|
+
|
|
637
|
+
<div style="background: #f8f9fa; padding: 30px; border-radius: 0 0 12px 12px; border: 1px solid #e9ecef; border-top: none;">
|
|
638
|
+
<p style="margin-top: 0;">Hi${to.name ? ` ${to.name}` : ''},</p>
|
|
639
|
+
|
|
640
|
+
<p>Your 7-day trial ends tomorrow. After that, your dashboard becomes read-only and all monitoring services will pause:</p>
|
|
641
|
+
|
|
642
|
+
<div style="background: #fff5f5; border: 1px solid #fecaca; border-radius: 8px; padding: 20px; margin: 20px 0;">
|
|
643
|
+
<ul style="margin: 0; padding-left: 20px;">
|
|
644
|
+
<li><strong>Domain monitoring</strong>${domainsAdded > 0 ? ` (${domainsAdded} domain${domainsAdded > 1 ? 's' : ''} currently tracked)` : ''} — paused</li>
|
|
645
|
+
<li><strong>Weekly auto-validation</strong>${validationsRun > 0 ? ` (${validationsRun} validation${validationsRun > 1 ? 's' : ''} run so far)` : ''} — stopped</li>
|
|
646
|
+
<li><strong>AI agent analytics</strong> — no new data</li>
|
|
647
|
+
<li><strong>Score change alerts</strong> — disabled</li>
|
|
648
|
+
<li><strong>Dashboard access</strong> — read-only, content blurred</li>
|
|
649
|
+
</ul>
|
|
650
|
+
</div>
|
|
651
|
+
|
|
652
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
653
|
+
<p style="margin-top: 0; font-size: 14px; color: #666;">Continue with the Starter plan</p>
|
|
654
|
+
<p style="font-size: 32px; font-weight: bold; color: #2E86AB; margin: 8px 0;">$9<span style="font-size: 16px; font-weight: normal;">/month</span></p>
|
|
655
|
+
<p style="margin-bottom: 0; font-size: 14px; color: #666;">Monitor up to 3 domains • Cancel anytime</p>
|
|
656
|
+
</div>
|
|
657
|
+
|
|
658
|
+
<p style="text-align: center; margin: 30px 0;">
|
|
659
|
+
<a href="${dashboardUrl}/subscription" style="display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #2E86AB 0%, #47C97A 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">Subscribe Now</a>
|
|
660
|
+
</p>
|
|
661
|
+
|
|
662
|
+
${getEmailFooter()}
|
|
663
|
+
</div>
|
|
664
|
+
</body>
|
|
665
|
+
</html>
|
|
666
|
+
`;
|
|
667
|
+
const text = `
|
|
668
|
+
Your Trial Ends Tomorrow
|
|
669
|
+
|
|
670
|
+
Hi${to.name ? ` ${to.name}` : ''},
|
|
671
|
+
|
|
672
|
+
Your 7-day trial ends tomorrow. After that, your dashboard becomes read-only and all monitoring services will pause:
|
|
673
|
+
|
|
674
|
+
- Domain monitoring${domainsAdded > 0 ? ` (${domainsAdded} domain${domainsAdded > 1 ? 's' : ''} currently tracked)` : ''} - paused
|
|
675
|
+
- Weekly auto-validation${validationsRun > 0 ? ` (${validationsRun} validation${validationsRun > 1 ? 's' : ''} run so far)` : ''} - stopped
|
|
676
|
+
- AI agent analytics - no new data
|
|
677
|
+
- Score change alerts - disabled
|
|
678
|
+
- Dashboard access - read-only, content blurred
|
|
679
|
+
|
|
680
|
+
STARTER PLAN: $9/month
|
|
681
|
+
Monitor up to 3 domains - Cancel anytime
|
|
682
|
+
|
|
683
|
+
Subscribe now: ${dashboardUrl}/subscription
|
|
684
|
+
|
|
685
|
+
---
|
|
686
|
+
UCP Tools - AI Commerce Readiness Platform
|
|
687
|
+
Questions? Reply to this email - we read everything.
|
|
688
|
+
`;
|
|
689
|
+
return sendEmail({
|
|
690
|
+
to: [to],
|
|
691
|
+
subject,
|
|
692
|
+
html,
|
|
693
|
+
text,
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Send trial expired email - Trial has ended, prompt to upgrade
|
|
698
|
+
*/
|
|
699
|
+
async function sendTrialExpiredEmail(params) {
|
|
700
|
+
const { to, dashboardUrl, domainsAdded = 0 } = params;
|
|
701
|
+
const subject = 'Your free trial has ended \u2014 upgrade to keep your data';
|
|
702
|
+
const html = `
|
|
703
|
+
<!DOCTYPE html>
|
|
704
|
+
<html>
|
|
705
|
+
<head>
|
|
706
|
+
<meta charset="utf-8">
|
|
707
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
708
|
+
</head>
|
|
709
|
+
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
710
|
+
${getEmailHeader('Your Trial Has Ended', '#dc2626 0%, #991b1b 100%')}
|
|
711
|
+
|
|
712
|
+
<div style="background: #f8f9fa; padding: 30px; border-radius: 0 0 12px 12px; border: 1px solid #e9ecef; border-top: none;">
|
|
713
|
+
<p style="margin-top: 0;">Hi${to.name ? ` ${to.name}` : ''},</p>
|
|
714
|
+
|
|
715
|
+
<p>Your 7-day free trial has ended. Your dashboard access has been paused, but <strong>your domains are still being monitored</strong>:</p>
|
|
716
|
+
|
|
717
|
+
<div style="background: #fff5f5; border: 1px solid #fecaca; border-radius: 8px; padding: 20px; margin: 20px 0;">
|
|
718
|
+
<ul style="margin: 0; padding-left: 20px;">
|
|
719
|
+
<li><strong>Domain monitoring</strong>${domainsAdded > 0 ? ` (${domainsAdded} domain${domainsAdded > 1 ? 's' : ''})` : ''} — still running</li>
|
|
720
|
+
<li><strong>Dashboard & analytics</strong> — blurred until you upgrade</li>
|
|
721
|
+
<li><strong>Score change alerts</strong> — paused</li>
|
|
722
|
+
<li><strong>All your data</strong> — safe and collecting</li>
|
|
723
|
+
</ul>
|
|
724
|
+
</div>
|
|
725
|
+
|
|
726
|
+
<p>Upgrade now and everything unlocks instantly — no data gap, no setup needed.</p>
|
|
727
|
+
|
|
728
|
+
<div style="background: white; border-radius: 8px; padding: 20px; margin: 20px 0; text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
729
|
+
<p style="margin-top: 0; font-size: 14px; color: #666;">Resume with the Starter plan</p>
|
|
730
|
+
<p style="font-size: 32px; font-weight: bold; color: #2E86AB; margin: 8px 0;">$9<span style="font-size: 16px; font-weight: normal;">/month</span></p>
|
|
731
|
+
<p style="margin-bottom: 0; font-size: 14px; color: #666;">Monitor up to 3 domains • Cancel anytime</p>
|
|
732
|
+
</div>
|
|
733
|
+
|
|
734
|
+
<p style="text-align: center; margin: 30px 0;">
|
|
735
|
+
<a href="${dashboardUrl}/subscription" style="display: inline-block; padding: 14px 28px; background: linear-gradient(135deg, #2E86AB 0%, #47C97A 100%); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">Upgrade Now</a>
|
|
736
|
+
</p>
|
|
737
|
+
|
|
738
|
+
${getEmailFooter()}
|
|
739
|
+
</div>
|
|
740
|
+
</body>
|
|
741
|
+
</html>
|
|
742
|
+
`;
|
|
743
|
+
const text = `Your Trial Has Ended
|
|
744
|
+
|
|
745
|
+
Hi${to.name ? ` ${to.name}` : ''},
|
|
746
|
+
|
|
747
|
+
Your 7-day free trial has ended. Your account has been downgraded to the free tier:
|
|
748
|
+
|
|
749
|
+
- Domain monitoring${domainsAdded > 0 ? ` (${domainsAdded} domain${domainsAdded > 1 ? 's' : ''})` : ''} - paused
|
|
750
|
+
- AI agent analytics - no longer collecting data
|
|
751
|
+
- Score change alerts - disabled
|
|
752
|
+
- Dashboard - analytics blurred
|
|
753
|
+
|
|
754
|
+
Your existing data is still safe. Upgrade now and everything picks up right where you left off.
|
|
755
|
+
|
|
756
|
+
STARTER PLAN: $9/month
|
|
757
|
+
Monitor up to 3 domains - Cancel anytime
|
|
758
|
+
|
|
759
|
+
Upgrade now: ${dashboardUrl}/subscription
|
|
760
|
+
|
|
761
|
+
---
|
|
762
|
+
UCP Tools - AI Commerce Readiness Platform
|
|
763
|
+
Questions? Reply to this email - we read everything.
|
|
764
|
+
`;
|
|
765
|
+
return sendEmail({
|
|
766
|
+
to: [to],
|
|
767
|
+
subject,
|
|
768
|
+
html,
|
|
769
|
+
text,
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
//# sourceMappingURL=email.js.map
|