@tracked/emails 0.1.4 → 0.2.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/emails/client-onboarded.d.ts +35 -0
- package/dist/emails/client-onboarded.d.ts.map +1 -0
- package/dist/emails/client-onboarded.js +152 -0
- package/dist/emails/client-onboarded.js.map +1 -0
- package/dist/emails/index.d.ts +1 -0
- package/dist/emails/index.d.ts.map +1 -1
- package/dist/emails/index.js +1 -0
- package/dist/emails/index.js.map +1 -1
- package/dist/emails/monthly-report.d.ts.map +1 -1
- package/dist/emails/monthly-report.js +31 -47
- package/dist/emails/monthly-report.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +20 -20
- package/src/components/content.tsx +351 -0
- package/src/components/index.ts +44 -0
- package/src/components/interactive.tsx +260 -0
- package/src/components/layout.tsx +217 -0
- package/src/components/tokens.ts +74 -0
- package/src/components/typography.tsx +148 -0
- package/src/emails/anniversary.tsx +133 -0
- package/src/emails/app-review-request.tsx +100 -0
- package/src/emails/bodyweight-goal-reached.tsx +202 -350
- package/src/emails/client-inactive-alert.tsx +130 -0
- package/src/emails/client-onboarded.tsx +272 -0
- package/src/emails/coach-invite.tsx +67 -250
- package/src/emails/coach-removed-client.tsx +36 -197
- package/src/emails/direct-message.tsx +69 -227
- package/src/emails/feature-discovery.tsx +82 -266
- package/src/emails/first-workout-assigned.tsx +52 -238
- package/src/emails/first-workout-completed.tsx +88 -294
- package/src/emails/inactive-reengagement.tsx +81 -0
- package/src/emails/index.tsx +1 -0
- package/src/emails/monthly-report.tsx +195 -525
- package/src/emails/new-follower.tsx +60 -238
- package/src/emails/nps-survey.tsx +149 -0
- package/src/emails/subscription-canceled.tsx +88 -294
- package/src/emails/support-email.tsx +33 -67
- package/src/emails/team-invite.tsx +47 -240
- package/src/emails/team-member-removed-email.tsx +23 -218
- package/src/emails/tracked-magic-link-activate.tsx +29 -237
- package/src/emails/tracked-magic-link.tsx +31 -251
- package/src/emails/week-one-checkin.tsx +108 -329
- package/src/emails/weekly-progress-digest.tsx +248 -0
- package/src/emails/welcome.tsx +58 -326
- package/src/index.ts +19 -2
- package/dist/emails/client-accepted-invitation.d.ts +0 -10
- package/dist/emails/client-accepted-invitation.d.ts.map +0 -1
- package/dist/emails/client-accepted-invitation.js +0 -99
- package/dist/emails/client-accepted-invitation.js.map +0 -1
- package/src/emails/client-accepted-invitation.tsx +0 -258
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Section, Text, Row, Column, Hr } from "@react-email/components";
|
|
2
3
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} from "@react-email/components";
|
|
4
|
+
EmailLayout,
|
|
5
|
+
EmailHeader,
|
|
6
|
+
EmailFooter,
|
|
7
|
+
MetricCard,
|
|
8
|
+
ChangeIndicator,
|
|
9
|
+
ListBox,
|
|
10
|
+
DataRow,
|
|
11
|
+
SectionHeading,
|
|
12
|
+
HighlightBanner,
|
|
13
|
+
SmallText,
|
|
14
|
+
colors,
|
|
15
|
+
} from "../components";
|
|
16
16
|
|
|
17
17
|
interface MuscleGroup {
|
|
18
18
|
muscleGroupId: string;
|
|
@@ -23,9 +23,7 @@ interface MuscleGroup {
|
|
|
23
23
|
interface MonthlyMetrics {
|
|
24
24
|
accountId: number;
|
|
25
25
|
email: string;
|
|
26
|
-
reportMonth: string;
|
|
27
|
-
|
|
28
|
-
// Metrics
|
|
26
|
+
reportMonth: string;
|
|
29
27
|
avgStepCount: number | null;
|
|
30
28
|
avgBodyweight: number | null;
|
|
31
29
|
bodyweightChange: number | null;
|
|
@@ -33,8 +31,6 @@ interface MonthlyMetrics {
|
|
|
33
31
|
totalSetsTracked: number | null;
|
|
34
32
|
totalSessionsTracked: number | null;
|
|
35
33
|
topMuscleGroups: Array<MuscleGroup> | null;
|
|
36
|
-
|
|
37
|
-
// Survey metrics (optional)
|
|
38
34
|
avgReadinessEnergy: number | null;
|
|
39
35
|
avgReadinessMood: number | null;
|
|
40
36
|
avgReadinessStress: number | null;
|
|
@@ -59,10 +55,9 @@ interface MonthlyReportEmailProps {
|
|
|
59
55
|
metrics?: MonthlyMetrics;
|
|
60
56
|
previousMetrics?: PreviousMetrics | null;
|
|
61
57
|
websiteUrl?: string;
|
|
58
|
+
unsubscribeUrl?: string;
|
|
62
59
|
}
|
|
63
60
|
|
|
64
|
-
const baseUrl = "https://tracked.gg/android-chrome-192x192.png";
|
|
65
|
-
|
|
66
61
|
function formatNumber(num: number | null): string {
|
|
67
62
|
if (num === null) return "N/A";
|
|
68
63
|
return num.toLocaleString();
|
|
@@ -119,32 +114,10 @@ export const MonthlyReportEmail = ({
|
|
|
119
114
|
metrics = defaultMetrics,
|
|
120
115
|
previousMetrics = defaultPreviousMetrics,
|
|
121
116
|
websiteUrl = "https://tracked.gg",
|
|
117
|
+
unsubscribeUrl,
|
|
122
118
|
}: MonthlyReportEmailProps) => {
|
|
123
119
|
const monthName = getMonthName(metrics.reportMonth);
|
|
124
120
|
|
|
125
|
-
const getChangeIndicator = (
|
|
126
|
-
current: number | null,
|
|
127
|
-
previous: number | null,
|
|
128
|
-
): React.ReactNode => {
|
|
129
|
-
if (current === null || previous === null || !previousMetrics) return null;
|
|
130
|
-
const diff = current - previous;
|
|
131
|
-
if (diff > 0) {
|
|
132
|
-
return (
|
|
133
|
-
<Text style={changeIndicatorPositive}>
|
|
134
|
-
▲ {formatDecimal(Math.abs(diff))}
|
|
135
|
-
</Text>
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
if (diff < 0) {
|
|
139
|
-
return (
|
|
140
|
-
<Text style={changeIndicatorNegative}>
|
|
141
|
-
▼ {formatDecimal(Math.abs(diff))}
|
|
142
|
-
</Text>
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
return <Text style={changeIndicatorNeutral}>—</Text>;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
121
|
const hasBodyweightData = metrics.avgBodyweight || metrics.bodyweightChange;
|
|
149
122
|
const hasSessionInsights =
|
|
150
123
|
metrics.avgReadinessEnergy ||
|
|
@@ -153,493 +126,190 @@ export const MonthlyReportEmail = ({
|
|
|
153
126
|
metrics.avgSessionProgress;
|
|
154
127
|
|
|
155
128
|
return (
|
|
156
|
-
<
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
<
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
129
|
+
<EmailLayout
|
|
130
|
+
preview={`Your ${monthName} Training Report - Your monthly fitness journey at a glance`}
|
|
131
|
+
>
|
|
132
|
+
<EmailHeader />
|
|
133
|
+
|
|
134
|
+
<HighlightBanner>
|
|
135
|
+
<Text
|
|
136
|
+
style={{
|
|
137
|
+
color: "#ffffff",
|
|
138
|
+
margin: "0",
|
|
139
|
+
fontSize: "20px",
|
|
140
|
+
fontWeight: "700" as const,
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
Your {monthName} Training Report
|
|
144
|
+
</Text>
|
|
145
|
+
</HighlightBanner>
|
|
146
|
+
|
|
147
|
+
{/* Activity Overview */}
|
|
148
|
+
<Section style={{ marginBottom: "24px" }}>
|
|
149
|
+
<SectionHeading>Activity Overview</SectionHeading>
|
|
150
|
+
<MetricCard
|
|
151
|
+
label="Average Daily Steps"
|
|
152
|
+
value={formatNumber(metrics.avgStepCount)}
|
|
153
|
+
change={
|
|
154
|
+
<ChangeIndicator
|
|
155
|
+
value={metrics.avgStepCount}
|
|
156
|
+
previousValue={previousMetrics?.avgStepCount || null}
|
|
157
|
+
/>
|
|
158
|
+
}
|
|
159
|
+
/>
|
|
160
|
+
</Section>
|
|
161
|
+
|
|
162
|
+
{/* Bodyweight Progress */}
|
|
163
|
+
{hasBodyweightData && (
|
|
164
|
+
<>
|
|
165
|
+
<Hr style={{ borderColor: colors.border, margin: "24px 0" }} />
|
|
166
|
+
<Section style={{ marginBottom: "24px" }}>
|
|
167
|
+
<SectionHeading>Bodyweight Trends</SectionHeading>
|
|
168
|
+
<Row>
|
|
169
|
+
<Column style={{ width: "48%" }}>
|
|
170
|
+
<MetricCard
|
|
171
|
+
label="Average Bodyweight"
|
|
172
|
+
value={`${formatDecimal(metrics.avgBodyweight, 1)} lbs`}
|
|
173
|
+
size="small"
|
|
174
|
+
change={
|
|
175
|
+
<ChangeIndicator
|
|
176
|
+
value={metrics.avgBodyweight}
|
|
177
|
+
previousValue={previousMetrics?.avgBodyweight || null}
|
|
178
|
+
/>
|
|
179
|
+
}
|
|
180
|
+
/>
|
|
169
181
|
</Column>
|
|
170
|
-
<Column
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
<Column style={{ width: "4%" }} />
|
|
183
|
+
<Column style={{ width: "48%" }}>
|
|
184
|
+
<MetricCard
|
|
185
|
+
label="Monthly Change"
|
|
186
|
+
value={
|
|
187
|
+
metrics.bodyweightChange !== null
|
|
188
|
+
? (metrics.bodyweightChange >= 0 ? "+" : "") +
|
|
189
|
+
formatDecimal(metrics.bodyweightChange, 1) +
|
|
190
|
+
" lbs"
|
|
191
|
+
: "N/A"
|
|
192
|
+
}
|
|
193
|
+
size="small"
|
|
194
|
+
/>
|
|
178
195
|
</Column>
|
|
179
196
|
</Row>
|
|
180
|
-
|
|
181
|
-
{/* Purple Gradient Section */}
|
|
182
|
-
<Section style={headerSection}>
|
|
183
|
-
<Text style={headerTitle}>Your {monthName} Training Report</Text>
|
|
184
|
-
<Text style={headerSubtitle}>
|
|
185
|
-
Your monthly fitness journey at a glance
|
|
186
|
-
</Text>
|
|
187
|
-
</Section>
|
|
188
|
-
|
|
189
|
-
<Hr style={hr} />
|
|
190
|
-
|
|
191
|
-
{/* Activity Overview */}
|
|
192
|
-
<Section style={sectionContainer}>
|
|
193
|
-
<Text style={sectionHeading}>📊 Activity Overview</Text>
|
|
194
|
-
<Section style={metricBox}>
|
|
195
|
-
<Text style={metricLabel}>Average Daily Steps</Text>
|
|
196
|
-
<Row>
|
|
197
|
-
<Column>
|
|
198
|
-
<Text style={metricValue}>
|
|
199
|
-
{formatNumber(metrics.avgStepCount)}
|
|
200
|
-
</Text>
|
|
201
|
-
</Column>
|
|
202
|
-
{getChangeIndicator(
|
|
203
|
-
metrics.avgStepCount,
|
|
204
|
-
previousMetrics?.avgStepCount || null,
|
|
205
|
-
)}
|
|
206
|
-
</Row>
|
|
207
|
-
</Section>
|
|
208
|
-
</Section>
|
|
209
|
-
|
|
210
|
-
{/* Bodyweight Progress */}
|
|
211
|
-
{hasBodyweightData && (
|
|
212
|
-
<>
|
|
213
|
-
<Hr style={hr} />
|
|
214
|
-
<Section style={sectionContainer}>
|
|
215
|
-
<Text style={sectionHeading}>⚖️ Bodyweight Trends</Text>
|
|
216
|
-
<Row>
|
|
217
|
-
<Column style={{ width: "48%" }}>
|
|
218
|
-
<Section style={metricBox}>
|
|
219
|
-
<Text style={metricLabel}>Average Bodyweight</Text>
|
|
220
|
-
<Row>
|
|
221
|
-
<Column>
|
|
222
|
-
<Text style={metricValueSmall}>
|
|
223
|
-
{formatDecimal(metrics.avgBodyweight, 1)} lbs
|
|
224
|
-
</Text>
|
|
225
|
-
</Column>
|
|
226
|
-
{getChangeIndicator(
|
|
227
|
-
metrics.avgBodyweight,
|
|
228
|
-
previousMetrics?.avgBodyweight || null,
|
|
229
|
-
)}
|
|
230
|
-
</Row>
|
|
231
|
-
</Section>
|
|
232
|
-
</Column>
|
|
233
|
-
<Column style={{ width: "4%" }} />
|
|
234
|
-
<Column style={{ width: "48%" }}>
|
|
235
|
-
<Section style={metricBox}>
|
|
236
|
-
<Text style={metricLabel}>Monthly Change</Text>
|
|
237
|
-
<Text style={metricValueSmall}>
|
|
238
|
-
{metrics.bodyweightChange !== null
|
|
239
|
-
? (metrics.bodyweightChange >= 0 ? "+" : "") +
|
|
240
|
-
formatDecimal(metrics.bodyweightChange, 1) +
|
|
241
|
-
" lbs"
|
|
242
|
-
: "N/A"}
|
|
243
|
-
</Text>
|
|
244
|
-
</Section>
|
|
245
|
-
</Column>
|
|
246
|
-
</Row>
|
|
247
|
-
</Section>
|
|
248
|
-
</>
|
|
249
|
-
)}
|
|
250
|
-
|
|
251
|
-
{/* Training Summary */}
|
|
252
|
-
<Hr style={hr} />
|
|
253
|
-
<Section style={sectionContainer}>
|
|
254
|
-
<Text style={sectionHeading}>💪 Training Summary</Text>
|
|
255
|
-
<Row>
|
|
256
|
-
<Column style={{ width: "48%" }}>
|
|
257
|
-
<Section style={metricBox}>
|
|
258
|
-
<Text style={metricLabel}>Sessions Per Week</Text>
|
|
259
|
-
<Row>
|
|
260
|
-
<Column>
|
|
261
|
-
<Text style={metricValueSmall}>
|
|
262
|
-
{formatDecimal(metrics.avgSessionsPerWeek, 1)}
|
|
263
|
-
</Text>
|
|
264
|
-
</Column>
|
|
265
|
-
{getChangeIndicator(
|
|
266
|
-
metrics.avgSessionsPerWeek,
|
|
267
|
-
previousMetrics?.avgSessionsPerWeek || null,
|
|
268
|
-
)}
|
|
269
|
-
</Row>
|
|
270
|
-
</Section>
|
|
271
|
-
</Column>
|
|
272
|
-
<Column style={{ width: "4%" }} />
|
|
273
|
-
<Column style={{ width: "48%" }}>
|
|
274
|
-
<Section style={metricBox}>
|
|
275
|
-
<Text style={metricLabel}>Total Sessions</Text>
|
|
276
|
-
<Row>
|
|
277
|
-
<Column>
|
|
278
|
-
<Text style={metricValueSmall}>
|
|
279
|
-
{formatNumber(metrics.totalSessionsTracked)}
|
|
280
|
-
</Text>
|
|
281
|
-
</Column>
|
|
282
|
-
{getChangeIndicator(
|
|
283
|
-
metrics.totalSessionsTracked,
|
|
284
|
-
previousMetrics?.totalSessionsTracked || null,
|
|
285
|
-
)}
|
|
286
|
-
</Row>
|
|
287
|
-
</Section>
|
|
288
|
-
</Column>
|
|
289
|
-
</Row>
|
|
290
|
-
<Section style={{ ...metricBox, marginTop: "10px" }}>
|
|
291
|
-
<Text style={metricLabel}>Total Sets Tracked</Text>
|
|
292
|
-
<Row>
|
|
293
|
-
<Column>
|
|
294
|
-
<Text style={metricValue}>
|
|
295
|
-
{formatNumber(metrics.totalSetsTracked)}
|
|
296
|
-
</Text>
|
|
297
|
-
</Column>
|
|
298
|
-
{getChangeIndicator(
|
|
299
|
-
metrics.totalSetsTracked,
|
|
300
|
-
previousMetrics?.totalSetsTracked || null,
|
|
301
|
-
)}
|
|
302
|
-
</Row>
|
|
303
|
-
</Section>
|
|
304
|
-
</Section>
|
|
305
|
-
|
|
306
|
-
{/* Top Muscle Groups */}
|
|
307
|
-
{metrics.topMuscleGroups && metrics.topMuscleGroups.length > 0 && (
|
|
308
|
-
<>
|
|
309
|
-
<Hr style={hr} />
|
|
310
|
-
<Section style={sectionContainer}>
|
|
311
|
-
<Text style={sectionHeading}>
|
|
312
|
-
🎯 Top 5 Prioritized Muscle Groups
|
|
313
|
-
</Text>
|
|
314
|
-
<Section style={listBox}>
|
|
315
|
-
{metrics.topMuscleGroups.map((mg, idx) => (
|
|
316
|
-
<Row
|
|
317
|
-
key={mg.muscleGroupId}
|
|
318
|
-
style={{
|
|
319
|
-
...listItem,
|
|
320
|
-
borderBottom:
|
|
321
|
-
idx < metrics.topMuscleGroups!.length - 1
|
|
322
|
-
? "1px solid #334155"
|
|
323
|
-
: "none",
|
|
324
|
-
}}
|
|
325
|
-
>
|
|
326
|
-
<Column>
|
|
327
|
-
<Text style={listItemName}>
|
|
328
|
-
{idx + 1}. {mg.name}
|
|
329
|
-
</Text>
|
|
330
|
-
</Column>
|
|
331
|
-
<Column style={{ textAlign: "right" }}>
|
|
332
|
-
<Text style={listItemValue}>
|
|
333
|
-
{formatNumber(mg.sets)} sets
|
|
334
|
-
</Text>
|
|
335
|
-
</Column>
|
|
336
|
-
</Row>
|
|
337
|
-
))}
|
|
338
|
-
</Section>
|
|
339
|
-
</Section>
|
|
340
|
-
</>
|
|
341
|
-
)}
|
|
342
|
-
|
|
343
|
-
{/* Session Insights */}
|
|
344
|
-
{hasSessionInsights && (
|
|
345
|
-
<>
|
|
346
|
-
<Hr style={hr} />
|
|
347
|
-
<Section style={sectionContainer}>
|
|
348
|
-
<Text style={sectionHeading}>📈 Session Insights</Text>
|
|
349
|
-
<Section style={listBox}>
|
|
350
|
-
{metrics.avgReadinessEnergy && (
|
|
351
|
-
<Row style={insightRow}>
|
|
352
|
-
<Column>
|
|
353
|
-
<Text style={insightLabel}>
|
|
354
|
-
Avg Readiness Energy
|
|
355
|
-
</Text>
|
|
356
|
-
</Column>
|
|
357
|
-
<Column style={{ textAlign: "right" }}>
|
|
358
|
-
<Text style={insightValue}>
|
|
359
|
-
{formatDecimal(metrics.avgReadinessEnergy)}/10
|
|
360
|
-
</Text>
|
|
361
|
-
</Column>
|
|
362
|
-
</Row>
|
|
363
|
-
)}
|
|
364
|
-
{metrics.avgReadinessMood && (
|
|
365
|
-
<Row style={insightRow}>
|
|
366
|
-
<Column>
|
|
367
|
-
<Text style={insightLabel}>Avg Readiness Mood</Text>
|
|
368
|
-
</Column>
|
|
369
|
-
<Column style={{ textAlign: "right" }}>
|
|
370
|
-
<Text style={insightValue}>
|
|
371
|
-
{formatDecimal(metrics.avgReadinessMood)}/10
|
|
372
|
-
</Text>
|
|
373
|
-
</Column>
|
|
374
|
-
</Row>
|
|
375
|
-
)}
|
|
376
|
-
{metrics.avgSessionSatisfaction && (
|
|
377
|
-
<Row style={insightRow}>
|
|
378
|
-
<Column>
|
|
379
|
-
<Text style={insightLabel}>
|
|
380
|
-
Avg Session Satisfaction
|
|
381
|
-
</Text>
|
|
382
|
-
</Column>
|
|
383
|
-
<Column style={{ textAlign: "right" }}>
|
|
384
|
-
<Text style={insightValue}>
|
|
385
|
-
{formatDecimal(metrics.avgSessionSatisfaction)}/10
|
|
386
|
-
</Text>
|
|
387
|
-
</Column>
|
|
388
|
-
</Row>
|
|
389
|
-
)}
|
|
390
|
-
{metrics.avgSessionProgress && (
|
|
391
|
-
<Row style={insightRow}>
|
|
392
|
-
<Column>
|
|
393
|
-
<Text style={insightLabel}>
|
|
394
|
-
Avg Session Progress
|
|
395
|
-
</Text>
|
|
396
|
-
</Column>
|
|
397
|
-
<Column style={{ textAlign: "right" }}>
|
|
398
|
-
<Text style={insightValue}>
|
|
399
|
-
{formatDecimal(metrics.avgSessionProgress)}/10
|
|
400
|
-
</Text>
|
|
401
|
-
</Column>
|
|
402
|
-
</Row>
|
|
403
|
-
)}
|
|
404
|
-
</Section>
|
|
405
|
-
</Section>
|
|
406
|
-
</>
|
|
407
|
-
)}
|
|
408
|
-
|
|
409
|
-
{/* Footer Message */}
|
|
410
|
-
<Hr style={hr} />
|
|
411
|
-
<Section style={footerMessageBox}>
|
|
412
|
-
<Text style={footerMessage}>
|
|
413
|
-
Keep up the great work! Every rep, every session, every step
|
|
414
|
-
counts towards your goals.
|
|
415
|
-
</Text>
|
|
416
|
-
<Text style={footerMessageSmall}>
|
|
417
|
-
You can manage your email preferences in the Tracked app
|
|
418
|
-
settings.
|
|
419
|
-
</Text>
|
|
420
|
-
</Section>
|
|
421
|
-
|
|
422
|
-
<Hr style={hr} />
|
|
423
|
-
|
|
424
|
-
{/* Footer Links */}
|
|
425
|
-
<Text style={footer}>
|
|
426
|
-
Copyright © {new Date().getFullYear()} Tracked Training Platform Inc. <br /> 9101 Horne
|
|
427
|
-
Street, Vancouver, BC
|
|
428
|
-
</Text>
|
|
429
|
-
|
|
430
|
-
<Container>
|
|
431
|
-
<Link
|
|
432
|
-
href={`${websiteUrl}/terms`}
|
|
433
|
-
style={{ ...footer, paddingRight: 10 }}
|
|
434
|
-
>
|
|
435
|
-
Terms
|
|
436
|
-
</Link>
|
|
437
|
-
<Link style={{ ...footer, paddingRight: 10 }}> | </Link>
|
|
438
|
-
<Link
|
|
439
|
-
href={`${websiteUrl}/privacy`}
|
|
440
|
-
style={{ ...footer, paddingRight: 10 }}
|
|
441
|
-
>
|
|
442
|
-
Privacy
|
|
443
|
-
</Link>
|
|
444
|
-
<Link style={{ ...footer, paddingRight: 10 }}> | </Link>
|
|
445
|
-
<Link
|
|
446
|
-
href={`${websiteUrl}/support`}
|
|
447
|
-
style={{ ...footer, paddingRight: 10 }}
|
|
448
|
-
>
|
|
449
|
-
Support
|
|
450
|
-
</Link>
|
|
451
|
-
</Container>
|
|
452
|
-
|
|
453
|
-
<Text style={footer}>
|
|
454
|
-
This is a service notification by the Tracked Training Platform.
|
|
455
|
-
</Text>
|
|
456
197
|
</Section>
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const changeIndicatorNegative = {
|
|
565
|
-
color: "#ef4444",
|
|
566
|
-
fontSize: "14px",
|
|
567
|
-
marginLeft: "8px",
|
|
568
|
-
display: "inline",
|
|
569
|
-
};
|
|
570
|
-
|
|
571
|
-
const changeIndicatorNeutral = {
|
|
572
|
-
color: "#6b7280",
|
|
573
|
-
fontSize: "14px",
|
|
574
|
-
marginLeft: "8px",
|
|
575
|
-
display: "inline",
|
|
576
|
-
};
|
|
577
|
-
|
|
578
|
-
const listBox = {
|
|
579
|
-
backgroundColor: "#1e293b", // slate-800
|
|
580
|
-
borderRadius: "8px",
|
|
581
|
-
overflow: "hidden",
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
const listItem = {
|
|
585
|
-
padding: "12px",
|
|
586
|
-
};
|
|
587
|
-
|
|
588
|
-
const listItemName = {
|
|
589
|
-
color: "#ffffff",
|
|
590
|
-
fontSize: "16px",
|
|
591
|
-
fontWeight: "600",
|
|
592
|
-
margin: "0",
|
|
593
|
-
};
|
|
594
|
-
|
|
595
|
-
const listItemValue = {
|
|
596
|
-
color: "#e2e8f0", // slate-200
|
|
597
|
-
fontSize: "16px",
|
|
598
|
-
margin: "0",
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
const insightRow = {
|
|
602
|
-
padding: "10px",
|
|
603
|
-
};
|
|
604
|
-
|
|
605
|
-
const insightLabel = {
|
|
606
|
-
color: "#94a3b8", // slate-400
|
|
607
|
-
fontSize: "14px",
|
|
608
|
-
margin: "0",
|
|
609
|
-
};
|
|
610
|
-
|
|
611
|
-
const insightValue = {
|
|
612
|
-
color: "#ffffff",
|
|
613
|
-
fontSize: "16px",
|
|
614
|
-
fontWeight: "600",
|
|
615
|
-
margin: "0",
|
|
616
|
-
};
|
|
617
|
-
|
|
618
|
-
const footerMessageBox = {
|
|
619
|
-
marginTop: "20px",
|
|
620
|
-
padding: "20px",
|
|
621
|
-
backgroundColor: "#1e293b", // slate-800
|
|
622
|
-
borderRadius: "8px",
|
|
623
|
-
textAlign: "center" as const,
|
|
624
|
-
};
|
|
625
|
-
|
|
626
|
-
const footerMessage = {
|
|
627
|
-
color: "#e2e8f0", // slate-200
|
|
628
|
-
fontSize: "14px",
|
|
629
|
-
margin: "0",
|
|
630
|
-
};
|
|
198
|
+
</>
|
|
199
|
+
)}
|
|
200
|
+
|
|
201
|
+
{/* Training Summary */}
|
|
202
|
+
<Hr style={{ borderColor: colors.border, margin: "24px 0" }} />
|
|
203
|
+
<Section style={{ marginBottom: "24px" }}>
|
|
204
|
+
<SectionHeading>Training Summary</SectionHeading>
|
|
205
|
+
<Row>
|
|
206
|
+
<Column style={{ width: "48%" }}>
|
|
207
|
+
<MetricCard
|
|
208
|
+
label="Sessions Per Week"
|
|
209
|
+
value={formatDecimal(metrics.avgSessionsPerWeek, 1)}
|
|
210
|
+
size="small"
|
|
211
|
+
change={
|
|
212
|
+
<ChangeIndicator
|
|
213
|
+
value={metrics.avgSessionsPerWeek}
|
|
214
|
+
previousValue={previousMetrics?.avgSessionsPerWeek || null}
|
|
215
|
+
/>
|
|
216
|
+
}
|
|
217
|
+
/>
|
|
218
|
+
</Column>
|
|
219
|
+
<Column style={{ width: "4%" }} />
|
|
220
|
+
<Column style={{ width: "48%" }}>
|
|
221
|
+
<MetricCard
|
|
222
|
+
label="Total Sessions"
|
|
223
|
+
value={formatNumber(metrics.totalSessionsTracked)}
|
|
224
|
+
size="small"
|
|
225
|
+
change={
|
|
226
|
+
<ChangeIndicator
|
|
227
|
+
value={metrics.totalSessionsTracked}
|
|
228
|
+
previousValue={previousMetrics?.totalSessionsTracked || null}
|
|
229
|
+
/>
|
|
230
|
+
}
|
|
231
|
+
/>
|
|
232
|
+
</Column>
|
|
233
|
+
</Row>
|
|
234
|
+
<Section style={{ marginTop: "10px" }}>
|
|
235
|
+
<MetricCard
|
|
236
|
+
label="Total Sets Tracked"
|
|
237
|
+
value={formatNumber(metrics.totalSetsTracked)}
|
|
238
|
+
change={
|
|
239
|
+
<ChangeIndicator
|
|
240
|
+
value={metrics.totalSetsTracked}
|
|
241
|
+
previousValue={previousMetrics?.totalSetsTracked || null}
|
|
242
|
+
/>
|
|
243
|
+
}
|
|
244
|
+
/>
|
|
245
|
+
</Section>
|
|
246
|
+
</Section>
|
|
247
|
+
|
|
248
|
+
{/* Top Muscle Groups */}
|
|
249
|
+
{metrics.topMuscleGroups && metrics.topMuscleGroups.length > 0 && (
|
|
250
|
+
<>
|
|
251
|
+
<Hr style={{ borderColor: colors.border, margin: "24px 0" }} />
|
|
252
|
+
<Section style={{ marginBottom: "24px" }}>
|
|
253
|
+
<SectionHeading>Top 5 Prioritized Muscle Groups</SectionHeading>
|
|
254
|
+
<ListBox>
|
|
255
|
+
{metrics.topMuscleGroups.map((mg, idx) => (
|
|
256
|
+
<DataRow
|
|
257
|
+
key={mg.muscleGroupId}
|
|
258
|
+
label={`${idx + 1}. ${mg.name}`}
|
|
259
|
+
value={`${formatNumber(mg.sets)} sets`}
|
|
260
|
+
isLast={idx === metrics.topMuscleGroups!.length - 1}
|
|
261
|
+
/>
|
|
262
|
+
))}
|
|
263
|
+
</ListBox>
|
|
264
|
+
</Section>
|
|
265
|
+
</>
|
|
266
|
+
)}
|
|
267
|
+
|
|
268
|
+
{/* Session Insights */}
|
|
269
|
+
{hasSessionInsights && (
|
|
270
|
+
<>
|
|
271
|
+
<Hr style={{ borderColor: colors.border, margin: "24px 0" }} />
|
|
272
|
+
<Section style={{ marginBottom: "24px" }}>
|
|
273
|
+
<SectionHeading>Session Insights</SectionHeading>
|
|
274
|
+
<ListBox>
|
|
275
|
+
{metrics.avgReadinessEnergy && (
|
|
276
|
+
<DataRow
|
|
277
|
+
label="Avg Readiness Energy"
|
|
278
|
+
value={`${formatDecimal(metrics.avgReadinessEnergy)}/10`}
|
|
279
|
+
/>
|
|
280
|
+
)}
|
|
281
|
+
{metrics.avgReadinessMood && (
|
|
282
|
+
<DataRow
|
|
283
|
+
label="Avg Readiness Mood"
|
|
284
|
+
value={`${formatDecimal(metrics.avgReadinessMood)}/10`}
|
|
285
|
+
/>
|
|
286
|
+
)}
|
|
287
|
+
{metrics.avgSessionSatisfaction && (
|
|
288
|
+
<DataRow
|
|
289
|
+
label="Avg Session Satisfaction"
|
|
290
|
+
value={`${formatDecimal(metrics.avgSessionSatisfaction)}/10`}
|
|
291
|
+
/>
|
|
292
|
+
)}
|
|
293
|
+
{metrics.avgSessionProgress && (
|
|
294
|
+
<DataRow
|
|
295
|
+
label="Avg Session Progress"
|
|
296
|
+
value={`${formatDecimal(metrics.avgSessionProgress)}/10`}
|
|
297
|
+
isLast
|
|
298
|
+
/>
|
|
299
|
+
)}
|
|
300
|
+
</ListBox>
|
|
301
|
+
</Section>
|
|
302
|
+
</>
|
|
303
|
+
)}
|
|
631
304
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
};
|
|
305
|
+
<Hr style={{ borderColor: colors.border, margin: "24px 0" }} />
|
|
306
|
+
<SmallText muted>
|
|
307
|
+
You can manage your email preferences in the Tracked app settings.
|
|
308
|
+
</SmallText>
|
|
637
309
|
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
lineHeight: "16px",
|
|
642
|
-
textAlign: "center" as const,
|
|
310
|
+
<EmailFooter websiteUrl={websiteUrl} marketing unsubscribeUrl={unsubscribeUrl} />
|
|
311
|
+
</EmailLayout>
|
|
312
|
+
);
|
|
643
313
|
};
|
|
644
314
|
|
|
645
315
|
export default MonthlyReportEmail;
|