@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
|
@@ -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;
|