@trm-market-pulse/utils 1.0.0 → 1.0.2
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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/notifications/email-factory.d.ts +7 -0
- package/dist/notifications/email-factory.d.ts.map +1 -0
- package/dist/notifications/email-factory.js +111 -0
- package/dist/notifications/email-factory.js.map +1 -0
- package/dist/notifications/email-template/email-template-generator.d.ts +11 -0
- package/dist/notifications/email-template/email-template-generator.d.ts.map +1 -0
- package/dist/notifications/email-template/email-template-generator.js +384 -0
- package/dist/notifications/email-template/email-template-generator.js.map +1 -0
- package/package.json +6 -3
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ export * from './validators/check-conditions';
|
|
|
2
2
|
export * from './notifications/notification-factory';
|
|
3
3
|
export * from './notifications/build-message';
|
|
4
4
|
export * from './notifications/subscription-update-factory';
|
|
5
|
+
export * from './notifications/email-factory';
|
|
6
|
+
export * from './notifications/email-template/email-template-generator';
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,+BAA+B,CAAC;AAK9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6CAA6C,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,+BAA+B,CAAC;AAK9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6CAA6C,CAAC;AAC5D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,yDAAyD,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -18,4 +18,6 @@ __exportStar(require("./validators/check-conditions"), exports);
|
|
|
18
18
|
__exportStar(require("./notifications/notification-factory"), exports);
|
|
19
19
|
__exportStar(require("./notifications/build-message"), exports);
|
|
20
20
|
__exportStar(require("./notifications/subscription-update-factory"), exports);
|
|
21
|
+
__exportStar(require("./notifications/email-factory"), exports);
|
|
22
|
+
__exportStar(require("./notifications/email-template/email-template-generator"), exports);
|
|
21
23
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAGA,gEAA8C;AAK9C,uEAAqD;AACrD,gEAA8C;AAC9C,8EAA4D"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAGA,gEAA8C;AAK9C,uEAAqD;AACrD,gEAA8C;AAC9C,8EAA4D;AAC5D,gEAA8C;AAC9C,0FAAwE"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Resend } from 'resend';
|
|
2
|
+
import { IConfigServer, IEmail } from '@trm-market-pulse/shared-types';
|
|
3
|
+
export declare const emailFactory: ({ resend, config }: {
|
|
4
|
+
resend: Resend;
|
|
5
|
+
config: Partial<IConfigServer>;
|
|
6
|
+
}) => IEmail;
|
|
7
|
+
//# sourceMappingURL=email-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-factory.d.ts","sourceRoot":"","sources":["../../src/notifications/email-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AAGvE,eAAO,MAAM,YAAY,GAAI,oBAAoB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAAA;CAAE,KAAG,MAoHpG,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.emailFactory = void 0;
|
|
4
|
+
const email_template_generator_1 = require("./email-template/email-template-generator");
|
|
5
|
+
const emailFactory = ({ resend, config }) => ({
|
|
6
|
+
sendPlainText: async ({ email, subject, text }) => {
|
|
7
|
+
await resend.emails.send({
|
|
8
|
+
from: `Tracker <noreply@${config.emailFromDomain}>`,
|
|
9
|
+
to: email,
|
|
10
|
+
subject,
|
|
11
|
+
text,
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
sendHtml: async ({ email, subject, html }) => {
|
|
15
|
+
await resend.emails.send({
|
|
16
|
+
from: `Tracker <noreply@${config.emailFromDomain}>`,
|
|
17
|
+
to: email,
|
|
18
|
+
subject,
|
|
19
|
+
html,
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
generateEmailTemplate: email_template_generator_1.generateEmailTemplate,
|
|
23
|
+
welcomeEmailTemplate: (userName, loginUrl) => {
|
|
24
|
+
return (0, email_template_generator_1.generateEmailTemplate)({
|
|
25
|
+
title: 'Welcome to Tracker',
|
|
26
|
+
description: 'Welcome to Tracker',
|
|
27
|
+
icon: '👋',
|
|
28
|
+
iconIsImage: false,
|
|
29
|
+
buttonText: 'Get Started',
|
|
30
|
+
buttonUrl: loginUrl,
|
|
31
|
+
buttonIcon: '🚀',
|
|
32
|
+
footerText: 'If you have any questions, feel free to reach out to our support team.',
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
passwordResetEmailTemplate: (resetUrl, expiresIn) => {
|
|
36
|
+
return (0, email_template_generator_1.generateEmailTemplate)({
|
|
37
|
+
title: 'Reset Your Password',
|
|
38
|
+
description: `We received a request to reset your password. Click the button below to create a new password. This link will expire in ${expiresIn}.`,
|
|
39
|
+
icon: '🔐',
|
|
40
|
+
iconIsImage: false,
|
|
41
|
+
buttonText: 'Reset Password',
|
|
42
|
+
buttonUrl: resetUrl,
|
|
43
|
+
buttonIcon: '🔑',
|
|
44
|
+
footerText: "If you didn't request this, you can safely ignore this email. Your password won't change.",
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
emailVerificationEmailTemplate: (verificationUrl) => {
|
|
48
|
+
return (0, email_template_generator_1.generateEmailTemplate)({
|
|
49
|
+
title: 'Verify Your Email',
|
|
50
|
+
description: 'Please verify your email address to complete your account setup.',
|
|
51
|
+
icon: '✉️',
|
|
52
|
+
buttonText: 'Verify Email',
|
|
53
|
+
buttonUrl: verificationUrl,
|
|
54
|
+
buttonIcon: '✓',
|
|
55
|
+
footerText: "If you didn't create an account, you can safely ignore this email.",
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
passwordChangedEmailTemplate: () => (0, email_template_generator_1.generateEmailTemplate)({
|
|
59
|
+
title: 'Password Changed',
|
|
60
|
+
description: 'Password changed',
|
|
61
|
+
icon: '✅',
|
|
62
|
+
iconIsImage: false,
|
|
63
|
+
}),
|
|
64
|
+
priceAlertEmailTemplate: (alertName, currentPrice, targetPrice, alertUrl) => (0, email_template_generator_1.generateEmailTemplate)({
|
|
65
|
+
title: 'Price Alert Triggered',
|
|
66
|
+
description: `Your price alert "${alertName}" has been triggered!`,
|
|
67
|
+
icon: '📊',
|
|
68
|
+
iconIsImage: false,
|
|
69
|
+
features: [
|
|
70
|
+
`Current Price: $${currentPrice.toFixed(2)}`,
|
|
71
|
+
`Target Price: $${targetPrice.toFixed(2)}`,
|
|
72
|
+
],
|
|
73
|
+
buttonText: 'View Alert',
|
|
74
|
+
buttonUrl: alertUrl,
|
|
75
|
+
buttonIcon: '🔔',
|
|
76
|
+
footerText: 'You can manage your alerts in your dashboard.',
|
|
77
|
+
}),
|
|
78
|
+
accountLockedEmailTemplate: (unlockUrl) => (0, email_template_generator_1.generateEmailTemplate)({
|
|
79
|
+
title: 'Account Temporarily Locked',
|
|
80
|
+
description: 'Your account has been temporarily locked due to multiple failed login attempts. Click below to unlock your account.',
|
|
81
|
+
icon: '🔒',
|
|
82
|
+
iconIsImage: false,
|
|
83
|
+
badgeText: 'Security',
|
|
84
|
+
buttonText: 'Unlock Account',
|
|
85
|
+
buttonUrl: unlockUrl,
|
|
86
|
+
buttonIcon: '🔓',
|
|
87
|
+
footerText: "If you didn't attempt to log in, please contact support immediately.",
|
|
88
|
+
}),
|
|
89
|
+
otpVerificationEmailTemplate: (otp) => (0, email_template_generator_1.generateEmailTemplate)({
|
|
90
|
+
title: 'OTP Verification',
|
|
91
|
+
description: `Your OTP is: ${otp}`,
|
|
92
|
+
icon: '✉️',
|
|
93
|
+
iconIsImage: false,
|
|
94
|
+
badgeText: 'Verification',
|
|
95
|
+
buttonText: 'Verify OTP',
|
|
96
|
+
buttonIcon: '✓',
|
|
97
|
+
footerText: "If you didn't request this, you can safely ignore this email.",
|
|
98
|
+
}),
|
|
99
|
+
emailVerifiedEmailTemplate: () => (0, email_template_generator_1.generateEmailTemplate)({
|
|
100
|
+
title: 'Email Verified',
|
|
101
|
+
description: 'Your email has been verified successfully.',
|
|
102
|
+
icon: '✅',
|
|
103
|
+
iconIsImage: false,
|
|
104
|
+
badgeText: 'Verification',
|
|
105
|
+
buttonText: 'Verify Email',
|
|
106
|
+
buttonIcon: '✓',
|
|
107
|
+
footerText: "If you didn't request this, you can safely ignore this email.",
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
exports.emailFactory = emailFactory;
|
|
111
|
+
//# sourceMappingURL=email-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-factory.js","sourceRoot":"","sources":["../../src/notifications/email-factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { IEmailTemplateOptions } from '@trm-market-pulse/shared-types';
|
|
2
|
+
export declare function generateEmailTemplate(options: IEmailTemplateOptions): string;
|
|
3
|
+
export declare const emailTemplates: {
|
|
4
|
+
welcome: (userName: string, loginUrl: string) => string;
|
|
5
|
+
passwordReset: (resetUrl: string, expiresIn?: string) => string;
|
|
6
|
+
emailVerification: (verificationUrl: string) => string;
|
|
7
|
+
passwordChanged: () => string;
|
|
8
|
+
priceAlert: (alertName: string, currentPrice: number, targetPrice: number, alertUrl: string) => string;
|
|
9
|
+
accountLocked: (unlockUrl: string) => string;
|
|
10
|
+
};
|
|
11
|
+
//# sourceMappingURL=email-template-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-template-generator.d.ts","sourceRoot":"","sources":["../../../src/notifications/email-template/email-template-generator.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAKvE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAsV5E;AAKD,eAAO,MAAM,cAAc;wBAIL,MAAM,YAAY,MAAM;8BAclB,MAAM,cAAa,MAAM;yCAed,MAAM;;4BA4BnB,MAAM,gBAAgB,MAAM,eAAe,MAAM,YAAY,MAAM;+BAkBhE,MAAM;CAYlC,CAAC"}
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.emailTemplates = void 0;
|
|
4
|
+
exports.generateEmailTemplate = generateEmailTemplate;
|
|
5
|
+
function generateEmailTemplate(options) {
|
|
6
|
+
const { title, description, icon, iconIsImage = false, badgeText, badgeIcon, features, buttonText, buttonUrl, buttonIcon, footerText, } = options;
|
|
7
|
+
const iconHtml = icon
|
|
8
|
+
? iconIsImage
|
|
9
|
+
? `<img src="${icon}" alt="" style="width: 32px; height: 32px; display: block;" />`
|
|
10
|
+
: `<span class="icon-emoji" style="font-size: 32px; line-height: 1;">${icon}</span>`
|
|
11
|
+
: '';
|
|
12
|
+
const iconWrapperHtml = icon
|
|
13
|
+
? `<div class="icon-wrapper" style="display: inline-block; padding: 16px; background-color: #e0f7f4; border-radius: 50%; margin-bottom: 16px;">
|
|
14
|
+
${iconHtml}
|
|
15
|
+
</div>`
|
|
16
|
+
: '';
|
|
17
|
+
const badgeHtml = badgeText
|
|
18
|
+
? `<span class="badge" style="display: inline-block; padding: 4px 12px; background-color: #525252; color: #fafafa; border-radius: 9999px; font-size: 12px; font-weight: 500; margin: 8px 0 0;">
|
|
19
|
+
${badgeIcon ? `<span class="badge-icon" style="display: inline-block; width: 12px; height: 12px; margin-right: 4px; vertical-align: middle;">${badgeIcon}</span>` : ''}
|
|
20
|
+
${badgeText}
|
|
21
|
+
</span>`
|
|
22
|
+
: '';
|
|
23
|
+
const descriptionHtml = description
|
|
24
|
+
? `<p class="email-description" style="font-size: 14px; color: #171717; margin: 12px 0 0; line-height: 1.5;">${description}</p>`
|
|
25
|
+
: '';
|
|
26
|
+
const featuresHtml = features && features.length > 0
|
|
27
|
+
? `<div class="features-section" style="text-align: left; margin-bottom: 16px;">
|
|
28
|
+
<p class="features-title" style="font-size: 14px; font-weight: 500; color: #a1a1a1; margin: 0 0 8px;">Planned features:</p>
|
|
29
|
+
<ul class="features-list" style="list-style: none; padding: 0; margin: 0;">
|
|
30
|
+
${features
|
|
31
|
+
.map(feature => `
|
|
32
|
+
<li class="feature-item" style="font-size: 14px; color: #a1a1a1; margin: 6px 0; padding-left: 20px; position: relative;">
|
|
33
|
+
<span class="feature-bullet" style="position: absolute; left: 0; top: 8px; width: 6px; height: 6px; background-color: rgba(25, 163, 120, 0.5); border-radius: 50%;"></span>
|
|
34
|
+
<span>${feature}</span>
|
|
35
|
+
</li>
|
|
36
|
+
`)
|
|
37
|
+
.join('')}
|
|
38
|
+
</ul>
|
|
39
|
+
</div>`
|
|
40
|
+
: '';
|
|
41
|
+
const buttonHtml = buttonText && buttonUrl
|
|
42
|
+
? `<div class="button-wrapper" style="margin-top: 16px;">
|
|
43
|
+
<a href="${buttonUrl}" class="email-button" style="display: inline-block; width: 100%; padding: 10px 16px; background-color: #fafafa; color: #171717; text-decoration: none; border: 1px solid #d4d4d4; border-radius: 6px; font-size: 14px; font-weight: 500; text-align: center; box-sizing: border-box;">
|
|
44
|
+
${buttonIcon ? `<span class="button-icon" style="display: inline-block; width: 16px; height: 16px; margin-right: 8px; vertical-align: middle;">${buttonIcon}</span>` : ''}
|
|
45
|
+
${buttonText}
|
|
46
|
+
</a>
|
|
47
|
+
</div>`
|
|
48
|
+
: '';
|
|
49
|
+
const footerHtml = footerText
|
|
50
|
+
? `<tr>
|
|
51
|
+
<td class="email-footer" style="padding: 16px 24px; text-align: center; font-size: 12px; color: #a1a1a1; border-top: 1px solid #d4d4d4;">
|
|
52
|
+
${footerText}
|
|
53
|
+
</td>
|
|
54
|
+
</tr>`
|
|
55
|
+
: '';
|
|
56
|
+
return `<!DOCTYPE html>
|
|
57
|
+
<html lang="en">
|
|
58
|
+
<head>
|
|
59
|
+
<meta charset="UTF-8">
|
|
60
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
61
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
62
|
+
<title>${title}</title>
|
|
63
|
+
<!--[if mso]>
|
|
64
|
+
<style type="text/css">
|
|
65
|
+
body, table, td {font-family: Arial, sans-serif !important;}
|
|
66
|
+
</style>
|
|
67
|
+
<![endif]-->
|
|
68
|
+
<style type="text/css">
|
|
69
|
+
/* Reset styles */
|
|
70
|
+
body, table, td, p, a, li, blockquote {
|
|
71
|
+
-webkit-text-size-adjust: 100%;
|
|
72
|
+
-ms-text-size-adjust: 100%;
|
|
73
|
+
}
|
|
74
|
+
table, td {
|
|
75
|
+
mso-table-lspace: 0pt;
|
|
76
|
+
mso-table-rspace: 0pt;
|
|
77
|
+
}
|
|
78
|
+
img {
|
|
79
|
+
-ms-interpolation-mode: bicubic;
|
|
80
|
+
border: 0;
|
|
81
|
+
outline: none;
|
|
82
|
+
text-decoration: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Base styles */
|
|
86
|
+
body {
|
|
87
|
+
margin: 0;
|
|
88
|
+
padding: 0;
|
|
89
|
+
width: 100% !important;
|
|
90
|
+
height: 100% !important;
|
|
91
|
+
background-color: #f5f5f5;
|
|
92
|
+
font-family: 'Work Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
93
|
+
font-size: 14px;
|
|
94
|
+
line-height: 1.6;
|
|
95
|
+
color: #171717;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Container */
|
|
99
|
+
.email-wrapper {
|
|
100
|
+
width: 100%;
|
|
101
|
+
background-color: #f5f5f5;
|
|
102
|
+
padding: 32px 16px;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Card container */
|
|
106
|
+
.email-container {
|
|
107
|
+
max-width: 448px;
|
|
108
|
+
width: 100%;
|
|
109
|
+
margin: 0 auto;
|
|
110
|
+
background-color: #fafafa;
|
|
111
|
+
border-radius: 12px;
|
|
112
|
+
overflow: hidden;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* Header section */
|
|
116
|
+
.email-header {
|
|
117
|
+
padding: 24px 24px 16px;
|
|
118
|
+
text-align: center;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.icon-wrapper {
|
|
122
|
+
display: inline-block;
|
|
123
|
+
padding: 16px;
|
|
124
|
+
background-color: #e0f7f4;
|
|
125
|
+
border-radius: 50%;
|
|
126
|
+
margin-bottom: 16px;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.icon-wrapper img {
|
|
130
|
+
width: 32px;
|
|
131
|
+
height: 32px;
|
|
132
|
+
display: block;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.icon-emoji {
|
|
136
|
+
font-size: 32px;
|
|
137
|
+
line-height: 1;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.email-title {
|
|
141
|
+
font-size: 20px;
|
|
142
|
+
font-weight: 600;
|
|
143
|
+
color: #171717;
|
|
144
|
+
margin: 0 0 8px;
|
|
145
|
+
line-height: 1.3;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.badge {
|
|
149
|
+
display: inline-block;
|
|
150
|
+
padding: 4px 12px;
|
|
151
|
+
background-color: #525252;
|
|
152
|
+
color: #fafafa;
|
|
153
|
+
border-radius: 9999px;
|
|
154
|
+
font-size: 12px;
|
|
155
|
+
font-weight: 500;
|
|
156
|
+
margin: 8px 0 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.badge-icon {
|
|
160
|
+
display: inline-block;
|
|
161
|
+
width: 12px;
|
|
162
|
+
height: 12px;
|
|
163
|
+
margin-right: 4px;
|
|
164
|
+
vertical-align: middle;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.email-description {
|
|
168
|
+
font-size: 14px;
|
|
169
|
+
color: #171717;
|
|
170
|
+
margin: 12px 0 0;
|
|
171
|
+
line-height: 1.5;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* Content section */
|
|
175
|
+
.email-content {
|
|
176
|
+
padding: 16px 24px 24px;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.features-section {
|
|
180
|
+
text-align: left;
|
|
181
|
+
margin-bottom: 16px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.features-title {
|
|
185
|
+
font-size: 14px;
|
|
186
|
+
font-weight: 500;
|
|
187
|
+
color: #a1a1a1;
|
|
188
|
+
margin: 0 0 8px;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.features-list {
|
|
192
|
+
list-style: none;
|
|
193
|
+
padding: 0;
|
|
194
|
+
margin: 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.feature-item {
|
|
198
|
+
font-size: 14px;
|
|
199
|
+
color: #a1a1a1;
|
|
200
|
+
margin: 6px 0;
|
|
201
|
+
padding-left: 20px;
|
|
202
|
+
position: relative;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.feature-bullet {
|
|
206
|
+
position: absolute;
|
|
207
|
+
left: 0;
|
|
208
|
+
top: 8px;
|
|
209
|
+
width: 6px;
|
|
210
|
+
height: 6px;
|
|
211
|
+
background-color: rgba(25, 163, 120, 0.5);
|
|
212
|
+
border-radius: 50%;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* Button */
|
|
216
|
+
.button-wrapper {
|
|
217
|
+
margin-top: 16px;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.email-button {
|
|
221
|
+
display: inline-block;
|
|
222
|
+
width: 100%;
|
|
223
|
+
padding: 10px 16px;
|
|
224
|
+
background-color: #fafafa;
|
|
225
|
+
color: #171717;
|
|
226
|
+
text-decoration: none;
|
|
227
|
+
border: 1px solid #d4d4d4;
|
|
228
|
+
border-radius: 6px;
|
|
229
|
+
font-size: 14px;
|
|
230
|
+
font-weight: 500;
|
|
231
|
+
text-align: center;
|
|
232
|
+
box-sizing: border-box;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.email-button:hover {
|
|
236
|
+
background-color: #f5f5f5;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.button-icon {
|
|
240
|
+
display: inline-block;
|
|
241
|
+
width: 16px;
|
|
242
|
+
height: 16px;
|
|
243
|
+
margin-right: 8px;
|
|
244
|
+
vertical-align: middle;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/* Footer */
|
|
248
|
+
.email-footer {
|
|
249
|
+
padding: 16px 24px;
|
|
250
|
+
text-align: center;
|
|
251
|
+
font-size: 12px;
|
|
252
|
+
color: #a1a1a1;
|
|
253
|
+
border-top: 1px solid #d4d4d4;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Responsive */
|
|
257
|
+
@media only screen and (max-width: 600px) {
|
|
258
|
+
.email-wrapper {
|
|
259
|
+
padding: 16px 8px;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.email-header {
|
|
263
|
+
padding: 20px 16px 12px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.email-content {
|
|
267
|
+
padding: 12px 16px 20px;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.email-title {
|
|
271
|
+
font-size: 18px;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.icon-wrapper {
|
|
275
|
+
padding: 12px;
|
|
276
|
+
margin-bottom: 12px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.icon-wrapper img,
|
|
280
|
+
.icon-emoji {
|
|
281
|
+
width: 24px;
|
|
282
|
+
height: 24px;
|
|
283
|
+
font-size: 24px;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
</style>
|
|
287
|
+
</head>
|
|
288
|
+
<body>
|
|
289
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" class="email-wrapper" style="width: 100%; background-color: #f5f5f5; padding: 32px 16px;">
|
|
290
|
+
<tr>
|
|
291
|
+
<td align="center">
|
|
292
|
+
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" border="0" class="email-container" style="max-width: 448px; width: 100%; background-color: #fafafa; border-radius: 12px;">
|
|
293
|
+
<!-- Header -->
|
|
294
|
+
<tr>
|
|
295
|
+
<td class="email-header" style="padding: 24px 24px 16px; text-align: center;">
|
|
296
|
+
${iconWrapperHtml}
|
|
297
|
+
|
|
298
|
+
<h1 class="email-title" style="font-size: 20px; font-weight: 600; color: #171717; margin: 0 0 8px; line-height: 1.3;">${title}</h1>
|
|
299
|
+
|
|
300
|
+
${badgeHtml}
|
|
301
|
+
|
|
302
|
+
${descriptionHtml}
|
|
303
|
+
</td>
|
|
304
|
+
</tr>
|
|
305
|
+
|
|
306
|
+
<!-- Content -->
|
|
307
|
+
<tr>
|
|
308
|
+
<td class="email-content" style="padding: 16px 24px 24px;">
|
|
309
|
+
${featuresHtml}
|
|
310
|
+
|
|
311
|
+
${buttonHtml}
|
|
312
|
+
</td>
|
|
313
|
+
</tr>
|
|
314
|
+
|
|
315
|
+
<!-- Footer -->
|
|
316
|
+
${footerHtml}
|
|
317
|
+
</table>
|
|
318
|
+
</td>
|
|
319
|
+
</tr>
|
|
320
|
+
</table>
|
|
321
|
+
</body>
|
|
322
|
+
</html>`;
|
|
323
|
+
}
|
|
324
|
+
exports.emailTemplates = {
|
|
325
|
+
welcome: (userName, loginUrl) => generateEmailTemplate({
|
|
326
|
+
title: 'Welcome to TRM Market Pulse!',
|
|
327
|
+
description: `Hi ${userName}, welcome to TRM Market Pulse! We're excited to have you on board.`,
|
|
328
|
+
icon: '👋',
|
|
329
|
+
buttonText: 'Get Started',
|
|
330
|
+
buttonUrl: loginUrl,
|
|
331
|
+
buttonIcon: '🚀',
|
|
332
|
+
footerText: 'If you have any questions, feel free to reach out to our support team.',
|
|
333
|
+
}),
|
|
334
|
+
passwordReset: (resetUrl, expiresIn = '1 hour') => generateEmailTemplate({
|
|
335
|
+
title: 'Reset Your Password',
|
|
336
|
+
description: `We received a request to reset your password. Click the button below to create a new password. This link will expire in ${expiresIn}.`,
|
|
337
|
+
icon: '🔐',
|
|
338
|
+
buttonText: 'Reset Password',
|
|
339
|
+
buttonUrl: resetUrl,
|
|
340
|
+
buttonIcon: '🔑',
|
|
341
|
+
footerText: "If you didn't request this, you can safely ignore this email. Your password won't change.",
|
|
342
|
+
}),
|
|
343
|
+
emailVerification: (verificationUrl) => generateEmailTemplate({
|
|
344
|
+
title: 'Verify Your Email',
|
|
345
|
+
description: 'Please verify your email address to complete your account setup.',
|
|
346
|
+
icon: '✉️',
|
|
347
|
+
buttonText: 'Verify Email',
|
|
348
|
+
buttonUrl: verificationUrl,
|
|
349
|
+
buttonIcon: '✓',
|
|
350
|
+
footerText: "If you didn't create an account, you can safely ignore this email.",
|
|
351
|
+
}),
|
|
352
|
+
passwordChanged: () => generateEmailTemplate({
|
|
353
|
+
title: 'Password Changed',
|
|
354
|
+
description: "Your password has been successfully changed. If you didn't make this change, please contact support immediately.",
|
|
355
|
+
icon: '✅',
|
|
356
|
+
badgeText: 'Security Alert',
|
|
357
|
+
badgeIcon: '🔒',
|
|
358
|
+
footerText: "If you didn't change your password, please secure your account immediately.",
|
|
359
|
+
}),
|
|
360
|
+
priceAlert: (alertName, currentPrice, targetPrice, alertUrl) => generateEmailTemplate({
|
|
361
|
+
title: 'Price Alert Triggered',
|
|
362
|
+
description: `Your price alert "${alertName}" has been triggered!`,
|
|
363
|
+
icon: '📊',
|
|
364
|
+
features: [
|
|
365
|
+
`Current Price: $${currentPrice.toFixed(2)}`,
|
|
366
|
+
`Target Price: $${targetPrice.toFixed(2)}`,
|
|
367
|
+
],
|
|
368
|
+
buttonText: 'View Alert',
|
|
369
|
+
buttonUrl: alertUrl,
|
|
370
|
+
buttonIcon: '🔔',
|
|
371
|
+
footerText: 'You can manage your alerts in your dashboard.',
|
|
372
|
+
}),
|
|
373
|
+
accountLocked: (unlockUrl) => generateEmailTemplate({
|
|
374
|
+
title: 'Account Temporarily Locked',
|
|
375
|
+
description: 'Your account has been temporarily locked due to multiple failed login attempts. Click below to unlock your account.',
|
|
376
|
+
icon: '🔒',
|
|
377
|
+
badgeText: 'Security',
|
|
378
|
+
buttonText: 'Unlock Account',
|
|
379
|
+
buttonUrl: unlockUrl,
|
|
380
|
+
buttonIcon: '🔓',
|
|
381
|
+
footerText: "If you didn't attempt to log in, please contact support immediately.",
|
|
382
|
+
}),
|
|
383
|
+
};
|
|
384
|
+
//# sourceMappingURL=email-template-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email-template-generator.js","sourceRoot":"","sources":["../../../src/notifications/email-template/email-template-generator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trm-market-pulse/utils",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Utils functions for Tracker projects",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -20,12 +20,15 @@
|
|
|
20
20
|
],
|
|
21
21
|
"author": "",
|
|
22
22
|
"license": "ISC",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"resend": "^6.6.0",
|
|
25
|
+
"moment-timezone": "^0.6.0"
|
|
26
|
+
},
|
|
23
27
|
"devDependencies": {
|
|
24
28
|
"typescript": "^5.9.3",
|
|
25
29
|
"@types/express": "^5.0.5",
|
|
26
30
|
"@types/node": "^24.10.0",
|
|
27
|
-
"@trm-market-pulse/shared-types": "workspace:*"
|
|
28
|
-
"moment-timezone": "^0.6.0"
|
|
31
|
+
"@trm-market-pulse/shared-types": "workspace:*"
|
|
29
32
|
},
|
|
30
33
|
"peerDependencies": {
|
|
31
34
|
"express": "^5.1.0",
|