@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.
Files changed (53) 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/emails/monthly-report.d.ts.map +1 -1
  10. package/dist/emails/monthly-report.js +31 -47
  11. package/dist/emails/monthly-report.js.map +1 -1
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +2 -2
  15. package/dist/index.js.map +1 -1
  16. package/package.json +20 -20
  17. package/src/components/content.tsx +351 -0
  18. package/src/components/index.ts +44 -0
  19. package/src/components/interactive.tsx +260 -0
  20. package/src/components/layout.tsx +217 -0
  21. package/src/components/tokens.ts +74 -0
  22. package/src/components/typography.tsx +148 -0
  23. package/src/emails/anniversary.tsx +133 -0
  24. package/src/emails/app-review-request.tsx +100 -0
  25. package/src/emails/bodyweight-goal-reached.tsx +202 -350
  26. package/src/emails/client-inactive-alert.tsx +130 -0
  27. package/src/emails/client-onboarded.tsx +272 -0
  28. package/src/emails/coach-invite.tsx +67 -250
  29. package/src/emails/coach-removed-client.tsx +36 -197
  30. package/src/emails/direct-message.tsx +69 -227
  31. package/src/emails/feature-discovery.tsx +82 -266
  32. package/src/emails/first-workout-assigned.tsx +52 -238
  33. package/src/emails/first-workout-completed.tsx +88 -294
  34. package/src/emails/inactive-reengagement.tsx +81 -0
  35. package/src/emails/index.tsx +1 -0
  36. package/src/emails/monthly-report.tsx +195 -525
  37. package/src/emails/new-follower.tsx +60 -238
  38. package/src/emails/nps-survey.tsx +149 -0
  39. package/src/emails/subscription-canceled.tsx +88 -294
  40. package/src/emails/support-email.tsx +33 -67
  41. package/src/emails/team-invite.tsx +47 -240
  42. package/src/emails/team-member-removed-email.tsx +23 -218
  43. package/src/emails/tracked-magic-link-activate.tsx +29 -237
  44. package/src/emails/tracked-magic-link.tsx +31 -251
  45. package/src/emails/week-one-checkin.tsx +108 -329
  46. package/src/emails/weekly-progress-digest.tsx +248 -0
  47. package/src/emails/welcome.tsx +58 -326
  48. package/src/index.ts +19 -2
  49. package/dist/emails/client-accepted-invitation.d.ts +0 -10
  50. package/dist/emails/client-accepted-invitation.d.ts.map +0 -1
  51. package/dist/emails/client-accepted-invitation.js +0 -99
  52. package/dist/emails/client-accepted-invitation.js.map +0 -1
  53. 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;