@tracked/emails 0.1.5 → 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.
Files changed (50) hide show
  1. package/dist/emails/client-onboarded.d.ts +35 -0
  2. package/dist/emails/client-onboarded.d.ts.map +1 -0
  3. package/dist/emails/client-onboarded.js +152 -0
  4. package/dist/emails/client-onboarded.js.map +1 -0
  5. package/dist/emails/index.d.ts +1 -0
  6. package/dist/emails/index.d.ts.map +1 -1
  7. package/dist/emails/index.js +1 -0
  8. package/dist/emails/index.js.map +1 -1
  9. package/dist/index.d.ts +2 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -2
  12. package/dist/index.js.map +1 -1
  13. package/package.json +20 -20
  14. package/src/components/content.tsx +351 -0
  15. package/src/components/index.ts +44 -0
  16. package/src/components/interactive.tsx +260 -0
  17. package/src/components/layout.tsx +217 -0
  18. package/src/components/tokens.ts +74 -0
  19. package/src/components/typography.tsx +148 -0
  20. package/src/emails/anniversary.tsx +133 -0
  21. package/src/emails/app-review-request.tsx +100 -0
  22. package/src/emails/bodyweight-goal-reached.tsx +202 -350
  23. package/src/emails/client-inactive-alert.tsx +130 -0
  24. package/src/emails/client-onboarded.tsx +272 -0
  25. package/src/emails/coach-invite.tsx +67 -250
  26. package/src/emails/coach-removed-client.tsx +36 -197
  27. package/src/emails/direct-message.tsx +69 -227
  28. package/src/emails/feature-discovery.tsx +82 -266
  29. package/src/emails/first-workout-assigned.tsx +52 -238
  30. package/src/emails/first-workout-completed.tsx +88 -294
  31. package/src/emails/inactive-reengagement.tsx +81 -0
  32. package/src/emails/index.tsx +1 -0
  33. package/src/emails/monthly-report.tsx +198 -520
  34. package/src/emails/new-follower.tsx +60 -238
  35. package/src/emails/nps-survey.tsx +149 -0
  36. package/src/emails/subscription-canceled.tsx +88 -294
  37. package/src/emails/support-email.tsx +33 -67
  38. package/src/emails/team-invite.tsx +47 -240
  39. package/src/emails/team-member-removed-email.tsx +23 -218
  40. package/src/emails/tracked-magic-link-activate.tsx +29 -237
  41. package/src/emails/tracked-magic-link.tsx +31 -251
  42. package/src/emails/week-one-checkin.tsx +108 -329
  43. package/src/emails/weekly-progress-digest.tsx +248 -0
  44. package/src/emails/welcome.tsx +58 -326
  45. package/src/index.ts +19 -2
  46. package/dist/emails/client-accepted-invitation.d.ts +0 -10
  47. package/dist/emails/client-accepted-invitation.d.ts.map +0 -1
  48. package/dist/emails/client-accepted-invitation.js +0 -99
  49. package/dist/emails/client-accepted-invitation.js.map +0 -1
  50. package/src/emails/client-accepted-invitation.tsx +0 -258
@@ -0,0 +1,130 @@
1
+ import * as React from "react";
2
+ import { Section, Img, Text } from "@react-email/components";
3
+ import {
4
+ EmailLayout,
5
+ EmailHeader,
6
+ EmailFooter,
7
+ Heading,
8
+ Paragraph,
9
+ PrimaryButton,
10
+ SecondaryButton,
11
+ colors,
12
+ spacing,
13
+ borderRadius,
14
+ } from "../components";
15
+
16
+ interface ClientInactiveAlertEmailProps {
17
+ coachName: string;
18
+ clientName: string;
19
+ clientEmail: string;
20
+ clientAvatarUrl?: string;
21
+ daysInactive: number;
22
+ lastWorkoutDate?: string;
23
+ clientProfileUrl: string;
24
+ messageUrl: string;
25
+ websiteUrl?: string;
26
+ }
27
+
28
+ export const ClientInactiveAlertEmail = ({
29
+ coachName = "Sarah Johnson",
30
+ clientName = "Alex Thompson",
31
+ clientEmail = "alex@example.com",
32
+ clientAvatarUrl = "https://cdn.trckd.ca/avatars/default.png",
33
+ daysInactive = 10,
34
+ lastWorkoutDate = "December 9, 2024",
35
+ clientProfileUrl = "tracked://app",
36
+ messageUrl = "tracked://app",
37
+ websiteUrl = "https://tracked.gg",
38
+ }: ClientInactiveAlertEmailProps) => {
39
+ const displayName = clientName || clientEmail;
40
+
41
+ const urgencyLevel = daysInactive >= 14 ? "high" : daysInactive >= 7 ? "medium" : "low";
42
+ const urgencyColor = urgencyLevel === "high" ? colors.error : urgencyLevel === "medium" ? colors.warning : colors.textSecondary;
43
+
44
+ return (
45
+ <EmailLayout preview={`${displayName} hasn't logged a workout in ${daysInactive} days`}>
46
+ <EmailHeader />
47
+
48
+ <Heading>Client Activity Alert</Heading>
49
+ <Paragraph>
50
+ Hi {coachName}, one of your clients needs a check-in.
51
+ </Paragraph>
52
+
53
+ {/* Client Card */}
54
+ <Section
55
+ style={{
56
+ backgroundColor: colors.surface,
57
+ padding: spacing.lg,
58
+ borderRadius: borderRadius.md,
59
+ margin: `${spacing.lg} 0`,
60
+ border: `1px solid ${colors.border}`,
61
+ }}
62
+ >
63
+ <table cellPadding="0" cellSpacing="0" style={{ width: "100%" }}>
64
+ <tr>
65
+ {clientAvatarUrl && (
66
+ <td style={{ width: "48px", verticalAlign: "top" }}>
67
+ <Img
68
+ src={clientAvatarUrl}
69
+ width="48"
70
+ height="48"
71
+ alt={displayName}
72
+ style={{
73
+ borderRadius: "50%",
74
+ border: `2px solid ${colors.border}`,
75
+ }}
76
+ />
77
+ </td>
78
+ )}
79
+ <td style={{ paddingLeft: clientAvatarUrl ? "16px" : "0", verticalAlign: "top" }}>
80
+ <Text
81
+ style={{
82
+ color: colors.textPrimary,
83
+ fontSize: "16px",
84
+ fontWeight: "bold",
85
+ margin: "0 0 4px 0",
86
+ }}
87
+ >
88
+ {displayName}
89
+ </Text>
90
+ <Text
91
+ style={{
92
+ color: urgencyColor,
93
+ fontSize: "14px",
94
+ fontWeight: "600",
95
+ margin: "0 0 4px 0",
96
+ }}
97
+ >
98
+ {daysInactive} days inactive
99
+ </Text>
100
+ {lastWorkoutDate && (
101
+ <Text
102
+ style={{
103
+ color: colors.textMuted,
104
+ fontSize: "13px",
105
+ margin: "0",
106
+ }}
107
+ >
108
+ Last workout: {lastWorkoutDate}
109
+ </Text>
110
+ )}
111
+ </td>
112
+ </tr>
113
+ </table>
114
+ </Section>
115
+
116
+ <Paragraph>
117
+ A quick message can make a big difference. Consider reaching out to see
118
+ how they're doing and if there's anything blocking their progress.
119
+ </Paragraph>
120
+
121
+ <PrimaryButton href={messageUrl}>Send a Message</PrimaryButton>
122
+
123
+ <SecondaryButton href={clientProfileUrl}>View Profile</SecondaryButton>
124
+
125
+ <EmailFooter websiteUrl={websiteUrl} />
126
+ </EmailLayout>
127
+ );
128
+ };
129
+
130
+ export default ClientInactiveAlertEmail;
@@ -0,0 +1,272 @@
1
+ import * as React from "react";
2
+ import { Section, Text, Row, Column } from "@react-email/components";
3
+ import {
4
+ EmailLayout,
5
+ EmailHeader,
6
+ EmailFooter,
7
+ Heading,
8
+ Paragraph,
9
+ TipBox,
10
+ PrimaryButton,
11
+ colors,
12
+ } from "../components";
13
+
14
+ interface ClientOnboardedEmailProps {
15
+ coachName: string;
16
+ clientName: string;
17
+ clientEmail: string;
18
+ clientProfileUrl: string;
19
+ websiteUrl?: string;
20
+ dateOfBirth?: string;
21
+ height?: string;
22
+ weight?: string;
23
+ timezone?: string;
24
+ unitPreference?: "metric" | "imperial";
25
+ currentGoal?: string;
26
+ currentWorkouts?: string[];
27
+ currentTrainingSplit?: string;
28
+ exerciseSelection?: string[];
29
+ equipmentListGym?: string[];
30
+ injuries?: string[];
31
+ avgCardioPerWeek?: string;
32
+ avgStepsPerDay?: string;
33
+ foodPreferences?: string[];
34
+ allergies?: string[];
35
+ dailyCalories?: string;
36
+ dailyMacros?: {
37
+ protein: string;
38
+ carbs: string;
39
+ fat: string;
40
+ };
41
+ avgNumberMealsPerDay?: string;
42
+ mealsYouUsuallyEat?: string[];
43
+ numMealsBeforeGym?: string;
44
+ hasProgressPhotos?: boolean;
45
+ }
46
+
47
+ const formatArray = (arr?: string[]): string => {
48
+ if (!arr || arr.length === 0) return "";
49
+ return arr.join(", ");
50
+ };
51
+
52
+ const DataRow = ({
53
+ label,
54
+ value,
55
+ }: {
56
+ label: string;
57
+ value: string | undefined;
58
+ }) => {
59
+ if (!value) return null;
60
+
61
+ return (
62
+ <Row style={{ marginBottom: "8px" }}>
63
+ <Column style={{ width: "140px", verticalAlign: "top" }}>
64
+ <Text
65
+ style={{
66
+ color: colors.textMuted,
67
+ fontSize: "13px",
68
+ lineHeight: "20px",
69
+ margin: "0",
70
+ fontWeight: "500" as const,
71
+ }}
72
+ >
73
+ {label}
74
+ </Text>
75
+ </Column>
76
+ <Column style={{ verticalAlign: "top" }}>
77
+ <Text
78
+ style={{
79
+ color: colors.textSecondary,
80
+ fontSize: "14px",
81
+ lineHeight: "20px",
82
+ margin: "0",
83
+ }}
84
+ >
85
+ {value}
86
+ </Text>
87
+ </Column>
88
+ </Row>
89
+ );
90
+ };
91
+
92
+ export const ClientOnboardedEmail = ({
93
+ coachName = "Sarah Johnson",
94
+ clientName = "Alex Thompson",
95
+ clientEmail = "alex@example.com",
96
+ clientProfileUrl = "tracked://app",
97
+ websiteUrl = "https://tracked.gg",
98
+ dateOfBirth = "March 15, 1992",
99
+ height = "5'10\" (178 cm)",
100
+ weight = "175 lbs (79 kg)",
101
+ timezone = "America/New_York",
102
+ unitPreference = "imperial",
103
+ currentGoal = "Build muscle and improve strength",
104
+ currentWorkouts = ["Push/Pull/Legs", "Compound movements"],
105
+ currentTrainingSplit = "6 days per week",
106
+ exerciseSelection = ["Barbell lifts", "Dumbbell work", "Cable exercises"],
107
+ equipmentListGym = ["Full gym access", "Barbells", "Dumbbells", "Cable machines"],
108
+ injuries = ["Previous lower back strain (recovered)"],
109
+ avgCardioPerWeek = "2-3 sessions, 20-30 minutes each",
110
+ avgStepsPerDay = "8,000-10,000",
111
+ foodPreferences = ["High protein", "Whole foods"],
112
+ allergies = ["None"],
113
+ dailyCalories = "2,800",
114
+ dailyMacros = { protein: "180", carbs: "300", fat: "85" },
115
+ avgNumberMealsPerDay = "4-5",
116
+ mealsYouUsuallyEat = ["Eggs and oatmeal", "Chicken and rice", "Protein shakes"],
117
+ numMealsBeforeGym = "2",
118
+ hasProgressPhotos = true,
119
+ }: ClientOnboardedEmailProps) => {
120
+ const displayName = clientName || clientEmail;
121
+
122
+ const hasPersonalData = dateOfBirth || height || weight || timezone;
123
+ const hasTrainingData =
124
+ currentGoal ||
125
+ (currentWorkouts && currentWorkouts.length > 0) ||
126
+ currentTrainingSplit ||
127
+ (exerciseSelection && exerciseSelection.length > 0) ||
128
+ (equipmentListGym && equipmentListGym.length > 0) ||
129
+ (injuries && injuries.length > 0) ||
130
+ avgCardioPerWeek ||
131
+ avgStepsPerDay;
132
+ const hasNutritionData =
133
+ (foodPreferences && foodPreferences.length > 0) ||
134
+ (allergies && allergies.length > 0) ||
135
+ dailyCalories ||
136
+ dailyMacros ||
137
+ avgNumberMealsPerDay ||
138
+ (mealsYouUsuallyEat && mealsYouUsuallyEat.length > 0) ||
139
+ numMealsBeforeGym;
140
+
141
+ const infoBoxStyle = {
142
+ backgroundColor: colors.surface,
143
+ padding: "20px 24px",
144
+ borderRadius: "8px",
145
+ margin: "24px 0",
146
+ border: `1px solid ${colors.border}`,
147
+ };
148
+
149
+ const sectionHeadingStyle = {
150
+ color: colors.accent,
151
+ fontSize: "14px",
152
+ fontWeight: "bold" as const,
153
+ textTransform: "uppercase" as const,
154
+ letterSpacing: "0.5px",
155
+ marginBottom: "16px",
156
+ marginTop: "0",
157
+ };
158
+
159
+ return (
160
+ <EmailLayout
161
+ preview={`${displayName} has completed onboarding and is ready to start training`}
162
+ >
163
+ <EmailHeader />
164
+
165
+ <Heading>New Client Onboarded!</Heading>
166
+ <Paragraph>
167
+ Hi {coachName}, {displayName} has completed their onboarding
168
+ questionnaire and is ready to start their training journey with you.
169
+ </Paragraph>
170
+
171
+ {/* Client Basic Info */}
172
+ <Section style={infoBoxStyle}>
173
+ <Text style={sectionHeadingStyle}>Client Details</Text>
174
+ <DataRow label="Name" value={displayName} />
175
+ <DataRow label="Email" value={clientEmail} />
176
+ {hasPersonalData && (
177
+ <>
178
+ <DataRow label="Date of Birth" value={dateOfBirth} />
179
+ <DataRow label="Height" value={height} />
180
+ <DataRow label="Weight" value={weight} />
181
+ <DataRow label="Timezone" value={timezone} />
182
+ <DataRow
183
+ label="Units"
184
+ value={
185
+ unitPreference === "metric"
186
+ ? "Metric (kg/cm)"
187
+ : unitPreference === "imperial"
188
+ ? "Imperial (lbs/ft)"
189
+ : undefined
190
+ }
191
+ />
192
+ </>
193
+ )}
194
+ </Section>
195
+
196
+ {/* Training Info */}
197
+ {hasTrainingData && (
198
+ <Section style={infoBoxStyle}>
199
+ <Text style={sectionHeadingStyle}>Training Information</Text>
200
+ <DataRow label="Current Goal" value={currentGoal} />
201
+ <DataRow
202
+ label="Current Workouts"
203
+ value={formatArray(currentWorkouts)}
204
+ />
205
+ <DataRow label="Training Split" value={currentTrainingSplit} />
206
+ <DataRow
207
+ label="Exercise Preferences"
208
+ value={formatArray(exerciseSelection)}
209
+ />
210
+ <DataRow
211
+ label="Available Equipment"
212
+ value={formatArray(equipmentListGym)}
213
+ />
214
+ <DataRow
215
+ label="Injuries/Limitations"
216
+ value={formatArray(injuries)}
217
+ />
218
+ <DataRow label="Cardio per Week" value={avgCardioPerWeek} />
219
+ <DataRow label="Daily Steps" value={avgStepsPerDay} />
220
+ </Section>
221
+ )}
222
+
223
+ {/* Nutrition Info */}
224
+ {hasNutritionData && (
225
+ <Section style={infoBoxStyle}>
226
+ <Text style={sectionHeadingStyle}>Nutrition Information</Text>
227
+ <DataRow
228
+ label="Food Preferences"
229
+ value={formatArray(foodPreferences)}
230
+ />
231
+ <DataRow label="Allergies" value={formatArray(allergies)} />
232
+ <DataRow label="Daily Calories" value={dailyCalories} />
233
+ {dailyMacros && (
234
+ <DataRow
235
+ label="Daily Macros"
236
+ value={`P: ${dailyMacros.protein}g | C: ${dailyMacros.carbs}g | F: ${dailyMacros.fat}g`}
237
+ />
238
+ )}
239
+ <DataRow label="Meals per Day" value={avgNumberMealsPerDay} />
240
+ <DataRow
241
+ label="Usual Meals"
242
+ value={formatArray(mealsYouUsuallyEat)}
243
+ />
244
+ <DataRow label="Pre-workout Meals" value={numMealsBeforeGym} />
245
+ </Section>
246
+ )}
247
+
248
+ {/* Progress Photos Notice */}
249
+ {hasProgressPhotos && (
250
+ <TipBox title="Progress Photos">
251
+ This client has uploaded progress photos. View them in their profile.
252
+ </TipBox>
253
+ )}
254
+
255
+ <Paragraph>
256
+ Review their profile and start building their personalized training
257
+ program.
258
+ </Paragraph>
259
+
260
+ <PrimaryButton href={clientProfileUrl}>View Client Profile</PrimaryButton>
261
+
262
+ <Paragraph>
263
+ Assign their first workout or check in to welcome them to your coaching
264
+ roster.
265
+ </Paragraph>
266
+
267
+ <EmailFooter websiteUrl={websiteUrl} />
268
+ </EmailLayout>
269
+ );
270
+ };
271
+
272
+ export default ClientOnboardedEmail;