wip
This commit is contained in:
@@ -3,22 +3,23 @@ import { EmailInvite, zEmailInvite } from './email-invite';
|
||||
import EmailResetPassword, {
|
||||
zEmailResetPassword,
|
||||
} from './email-reset-password';
|
||||
import TrailEndingSoon, { zTrailEndingSoon } from './trial-ending-soon';
|
||||
import OnboardingWelcome, {
|
||||
zOnboardingWelcome,
|
||||
} from './onboarding-welcome';
|
||||
import OnboardingWhatToTrack, {
|
||||
zOnboardingWhatToTrack,
|
||||
} from './onboarding-what-to-track';
|
||||
import OnboardingDashboards, {
|
||||
zOnboardingDashboards,
|
||||
} from './onboarding-dashboards';
|
||||
import OnboardingReplaceStack, {
|
||||
zOnboardingReplaceStack,
|
||||
} from './onboarding-replace-stack';
|
||||
import OnboardingFeatureRequest, {
|
||||
zOnboardingFeatureRequest,
|
||||
} from './onboarding-feature-request';
|
||||
import OnboardingTrialEnded, {
|
||||
zOnboardingTrialEnded,
|
||||
} from './onboarding-trial-ended';
|
||||
import OnboardingTrialEnding, {
|
||||
zOnboardingTrialEnding,
|
||||
} from './onboarding-trial-ending';
|
||||
import OnboardingWelcome, { zOnboardingWelcome } from './onboarding-welcome';
|
||||
import OnboardingWhatToTrack, {
|
||||
zOnboardingWhatToTrack,
|
||||
} from './onboarding-what-to-track';
|
||||
import TrailEndingSoon, { zTrailEndingSoon } from './trial-ending-soon';
|
||||
|
||||
export const templates = {
|
||||
invite: {
|
||||
@@ -38,31 +39,43 @@ export const templates = {
|
||||
'Your trial is ending soon',
|
||||
Component: TrailEndingSoon,
|
||||
schema: zTrailEndingSoon,
|
||||
category: 'billing' as const,
|
||||
},
|
||||
'onboarding-welcome': {
|
||||
subject: () => "You're in",
|
||||
Component: OnboardingWelcome,
|
||||
schema: zOnboardingWelcome,
|
||||
category: 'onboarding' as const,
|
||||
},
|
||||
'onboarding-what-to-track': {
|
||||
subject: () => "What's actually worth tracking",
|
||||
Component: OnboardingWhatToTrack,
|
||||
schema: zOnboardingWhatToTrack,
|
||||
category: 'onboarding' as const,
|
||||
},
|
||||
'onboarding-dashboards': {
|
||||
subject: () => 'The part most people skip',
|
||||
Component: OnboardingDashboards,
|
||||
schema: zOnboardingDashboards,
|
||||
category: 'onboarding' as const,
|
||||
},
|
||||
'onboarding-replace-stack': {
|
||||
'onboarding-featue-request': {
|
||||
subject: () => 'One provider to rule them all',
|
||||
Component: OnboardingReplaceStack,
|
||||
schema: zOnboardingReplaceStack,
|
||||
Component: OnboardingFeatureRequest,
|
||||
schema: zOnboardingFeatureRequest,
|
||||
category: 'onboarding' as const,
|
||||
},
|
||||
'onboarding-trial-ending': {
|
||||
subject: () => 'Your trial ends in a few days',
|
||||
Component: OnboardingTrialEnding,
|
||||
schema: zOnboardingTrialEnding,
|
||||
category: 'onboarding' as const,
|
||||
},
|
||||
'onboarding-trial-ended': {
|
||||
subject: () => 'Your trial has ended',
|
||||
Component: OnboardingTrialEnded,
|
||||
schema: zOnboardingTrialEnded,
|
||||
category: 'onboarding' as const,
|
||||
},
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Link, Text } from '@react-email/components';
|
||||
import React from 'react';
|
||||
import { z } from 'zod';
|
||||
import { Layout } from '../components/layout';
|
||||
import { List } from '../components/list';
|
||||
|
||||
export const zOnboardingDashboards = z.object({
|
||||
firstName: z.string().optional(),
|
||||
@@ -30,20 +31,34 @@ export function OnboardingDashboards({
|
||||
If you haven't yet, try building a simple dashboard. Pick one thing you
|
||||
care about and visualize it. Could be:
|
||||
</Text>
|
||||
<Text>
|
||||
- How many people sign up and then actually do something
|
||||
</Text>
|
||||
<Text>- Where users drop off in a flow (funnel)</Text>
|
||||
<Text>- Which pages lead to conversions (entry page → CTA)</Text>
|
||||
<List
|
||||
items={[
|
||||
'How many people sign up and then actually do something',
|
||||
'Where users drop off in a flow (funnel)',
|
||||
'Which pages lead to conversions (entry page → CTA)',
|
||||
]}
|
||||
/>
|
||||
<Text>
|
||||
This is usually when people go from "I have analytics" to "I understand
|
||||
what's happening." It's a different feeling.
|
||||
</Text>
|
||||
<Text>Takes maybe 10 minutes to set up. Worth it.</Text>
|
||||
<Text>
|
||||
<Link href={newUrl.toString()}>Create your first dashboard</Link>
|
||||
Best regards,
|
||||
<br />
|
||||
Carl
|
||||
</Text>
|
||||
<Text>Carl</Text>
|
||||
<span style={{ margin: '0 -20px', display: 'block' }}>
|
||||
<img
|
||||
src="https://openpanel.dev/_next/image?url=%2Fscreenshots%2Fdashboard-dark.webp&w=3840&q=75"
|
||||
alt="Dashboard"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: 'auto',
|
||||
borderRadius: '5px',
|
||||
}}
|
||||
/>
|
||||
</span>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
39
packages/email/src/emails/onboarding-feature-request.tsx
Normal file
39
packages/email/src/emails/onboarding-feature-request.tsx
Normal file
@@ -0,0 +1,39 @@
|
||||
import { Link, Text } from '@react-email/components';
|
||||
import React from 'react';
|
||||
import { z } from 'zod';
|
||||
import { Layout } from '../components/layout';
|
||||
|
||||
export const zOnboardingFeatureRequest = z.object({
|
||||
firstName: z.string().optional(),
|
||||
});
|
||||
|
||||
export type Props = z.infer<typeof zOnboardingFeatureRequest>;
|
||||
export default OnboardingFeatureRequest;
|
||||
export function OnboardingFeatureRequest({ firstName }: Props) {
|
||||
return (
|
||||
<Layout>
|
||||
<Text>Hi{firstName ? ` ${firstName}` : ''},</Text>
|
||||
<Text>
|
||||
OpenPanel aims to be the one stop shop for all your analytics needs.
|
||||
</Text>
|
||||
<Text>
|
||||
We have already in a very short time become one of the most popular
|
||||
open-source analytics platforms out there and we're working hard to add
|
||||
more features to make it the best analytics platform.
|
||||
</Text>
|
||||
<Text>
|
||||
Do you feel like you're missing a feature that's important to you? If
|
||||
that's the case, please reply here or go to our feedback board and add
|
||||
your request there.
|
||||
</Text>
|
||||
<Text>
|
||||
<Link href={'https://feedback.openpanel.dev'}>Feedback board</Link>
|
||||
</Text>
|
||||
<Text>
|
||||
Best regards,
|
||||
<br />
|
||||
Carl
|
||||
</Text>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { Text } from '@react-email/components';
|
||||
import React from 'react';
|
||||
import { z } from 'zod';
|
||||
import { Layout } from '../components/layout';
|
||||
|
||||
export const zOnboardingReplaceStack = z.object({
|
||||
firstName: z.string().optional(),
|
||||
});
|
||||
|
||||
export type Props = z.infer<typeof zOnboardingReplaceStack>;
|
||||
export default OnboardingReplaceStack;
|
||||
export function OnboardingReplaceStack({
|
||||
firstName,
|
||||
}: Props) {
|
||||
return (
|
||||
<Layout>
|
||||
<Text>Hi{firstName ? ` ${firstName}` : ''},</Text>
|
||||
<Text>
|
||||
A lot of people who sign up are using multiple tools: something for
|
||||
traffic, something for product analytics and something else for seeing
|
||||
raw events.
|
||||
</Text>
|
||||
<Text>OpenPanel can replace that whole setup.</Text>
|
||||
<Text>
|
||||
If you're still thinking of web analytics and product analytics as
|
||||
separate things, try combining them in a single dashboard. Traffic
|
||||
sources on top, user behavior below. That view tends to be more useful
|
||||
than either one alone.
|
||||
</Text>
|
||||
<Text>
|
||||
OpenPanel should be able to replace all of them, you can just reach out
|
||||
if you feel like something is missing.
|
||||
</Text>
|
||||
<Text>Carl</Text>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
55
packages/email/src/emails/onboarding-trial-ended.tsx
Normal file
55
packages/email/src/emails/onboarding-trial-ended.tsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Text } from '@react-email/components';
|
||||
import React from 'react';
|
||||
import { z } from 'zod';
|
||||
import { Button } from '../components/button';
|
||||
import { Layout } from '../components/layout';
|
||||
|
||||
export const zOnboardingTrialEnded = z.object({
|
||||
firstName: z.string().optional(),
|
||||
billingUrl: z.string(),
|
||||
recommendedPlan: z.string().optional(),
|
||||
});
|
||||
|
||||
export type Props = z.infer<typeof zOnboardingTrialEnded>;
|
||||
export default OnboardingTrialEnded;
|
||||
export function OnboardingTrialEnded({
|
||||
firstName,
|
||||
billingUrl = 'https://dashboard.openpanel.dev',
|
||||
recommendedPlan,
|
||||
}: Props) {
|
||||
const newUrl = new URL(billingUrl);
|
||||
newUrl.searchParams.set('utm_source', 'email');
|
||||
newUrl.searchParams.set('utm_medium', 'email');
|
||||
newUrl.searchParams.set('utm_campaign', 'onboarding-trial-ended');
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Text>Hi{firstName ? ` ${firstName}` : ''},</Text>
|
||||
<Text>Your OpenPanel trial has ended.</Text>
|
||||
<Text>
|
||||
Your tracking is still running in the background, but you won't be able
|
||||
to see any new data until you upgrade. All your dashboards, reports, and
|
||||
event history are still there waiting for you.
|
||||
</Text>
|
||||
<Text>
|
||||
Important: If you don't upgrade within 30 days, your workspace and
|
||||
projects will be permanently deleted.
|
||||
</Text>
|
||||
<Text>
|
||||
To keep your data and continue using OpenPanel, upgrade to a paid plan.{' '}
|
||||
{recommendedPlan
|
||||
? `Based on your usage we recommend upgrading to the ${recommendedPlan}`
|
||||
: 'Plans start at $2.50/month'}
|
||||
.
|
||||
</Text>
|
||||
<Text>
|
||||
If you have any questions or something's holding you back, just reply to
|
||||
this email.
|
||||
</Text>
|
||||
<Text>
|
||||
<Button href={newUrl.toString()}>Upgrade Now</Button>
|
||||
</Text>
|
||||
<Text>Carl</Text>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Button, Link, Text } from '@react-email/components';
|
||||
import { Text } from '@react-email/components';
|
||||
import React from 'react';
|
||||
import { z } from 'zod';
|
||||
import { Button } from '../components/button';
|
||||
import { Layout } from '../components/layout';
|
||||
|
||||
export const zOnboardingTrialEnding = z.object({
|
||||
@@ -33,32 +34,21 @@ export function OnboardingTrialEnding({
|
||||
event history) stays intact.
|
||||
</Text>
|
||||
<Text>
|
||||
If OpenPanel has been useful, upgrading just keeps it going. Plans
|
||||
start at $2.50/month
|
||||
{recommendedPlan ? ` and based on your usage we recommend ${recommendedPlan}` : ''}
|
||||
To continue using OpenPanel, you'll need to upgrade to a paid plan.{' '}
|
||||
{recommendedPlan
|
||||
? `Based on your usage we recommend upgrading to the ${recommendedPlan} plan`
|
||||
: 'Plans start at $2.50/month'}
|
||||
.
|
||||
</Text>
|
||||
<Text>
|
||||
If something's holding you back, I'd like to hear about it. Just
|
||||
reply.
|
||||
If something's holding you back, I'd like to hear about it. Just reply.
|
||||
</Text>
|
||||
<Text>
|
||||
Your project will recieve events for the next 30 days, if you haven't
|
||||
upgraded by then we'll remove your workspace and projects.
|
||||
</Text>
|
||||
<Text>
|
||||
<Button
|
||||
href={newUrl.toString()}
|
||||
style={{
|
||||
backgroundColor: '#0070f3',
|
||||
color: 'white',
|
||||
padding: '12px 20px',
|
||||
borderRadius: '5px',
|
||||
textDecoration: 'none',
|
||||
}}
|
||||
>
|
||||
Upgrade Now
|
||||
</Button>
|
||||
<Button href={newUrl.toString()}>Upgrade Now</Button>
|
||||
</Text>
|
||||
<Text>Carl</Text>
|
||||
</Layout>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Link, Text } from '@react-email/components';
|
||||
import { Heading, Link, Text } from '@react-email/components';
|
||||
import React from 'react';
|
||||
import { z } from 'zod';
|
||||
import { Layout } from '../components/layout';
|
||||
import { List } from '../components/list';
|
||||
|
||||
export const zOnboardingWelcome = z.object({
|
||||
firstName: z.string().optional(),
|
||||
@@ -10,34 +11,42 @@ export const zOnboardingWelcome = z.object({
|
||||
|
||||
export type Props = z.infer<typeof zOnboardingWelcome>;
|
||||
export default OnboardingWelcome;
|
||||
export function OnboardingWelcome({
|
||||
firstName,
|
||||
dashboardUrl = 'https://dashboard.openpanel.dev',
|
||||
}: Props) {
|
||||
const newUrl = new URL(dashboardUrl);
|
||||
newUrl.searchParams.set('utm_source', 'email');
|
||||
newUrl.searchParams.set('utm_medium', 'email');
|
||||
newUrl.searchParams.set('utm_campaign', 'onboarding-welcome');
|
||||
|
||||
export function OnboardingWelcome({ firstName }: Props) {
|
||||
return (
|
||||
<Layout>
|
||||
<Text>Hi{firstName ? ` ${firstName}` : ''},</Text>
|
||||
<Text>Thanks for trying OpenPanel.</Text>
|
||||
<Text>
|
||||
We built OpenPanel because most analytics tools are either too expensive,
|
||||
too complicated, or both. OpenPanel is different.
|
||||
We built OpenPanel because most analytics tools are either too
|
||||
expensive, too complicated, or both. OpenPanel is different.
|
||||
</Text>
|
||||
<Text>
|
||||
If you already have setup your tracking you should see your dashboard
|
||||
getting filled up. If you come from another provider and want to import
|
||||
your old events you can do that in our{' '}
|
||||
<Link href={newUrl.toString()}>project settings</Link>.
|
||||
We hope you find OpenPanel useful and if you have any questions,
|
||||
regarding tracking or how to import your existing events, just reach
|
||||
out. We're here to help.
|
||||
</Text>
|
||||
<Text>To get started, you can:</Text>
|
||||
<List
|
||||
items={[
|
||||
<Link
|
||||
key=""
|
||||
href={'https://openpanel.dev/docs/get-started/install-openpanel'}
|
||||
>
|
||||
Install tracking script
|
||||
</Link>,
|
||||
<Link
|
||||
key=""
|
||||
href={'https://openpanel.dev/docs/get-started/track-events'}
|
||||
>
|
||||
Start tracking your events
|
||||
</Link>,
|
||||
]}
|
||||
/>
|
||||
<Text>
|
||||
If you can't find your provider just reach out and we'll help you out.
|
||||
Best regards,
|
||||
<br />
|
||||
Carl
|
||||
</Text>
|
||||
<Text>Reach out if you have any questions. I answer all emails.</Text>
|
||||
<Text>Carl</Text>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Text } from '@react-email/components';
|
||||
import React from 'react';
|
||||
import { z } from 'zod';
|
||||
import { Layout } from '../components/layout';
|
||||
import { List } from '../components/list';
|
||||
|
||||
export const zOnboardingWhatToTrack = z.object({
|
||||
firstName: z.string().optional(),
|
||||
@@ -9,33 +10,34 @@ export const zOnboardingWhatToTrack = z.object({
|
||||
|
||||
export type Props = z.infer<typeof zOnboardingWhatToTrack>;
|
||||
export default OnboardingWhatToTrack;
|
||||
export function OnboardingWhatToTrack({
|
||||
firstName,
|
||||
}: Props) {
|
||||
export function OnboardingWhatToTrack({ firstName }: Props) {
|
||||
return (
|
||||
<Layout>
|
||||
<Text>Hi{firstName ? ` ${firstName}` : ''},</Text>
|
||||
<Text>
|
||||
Track the moments that tell you whether your product is working. Track
|
||||
things that matters to your product the most and then you can easily
|
||||
create funnels or conversions reports to understand what happening.
|
||||
Tracking can be overwhelming at first, and that's why its important to
|
||||
focus on what's matters. For most products, that's something like:
|
||||
</Text>
|
||||
<Text>For most products, that's something like:</Text>
|
||||
<Text>- Signups</Text>
|
||||
<List
|
||||
items={[
|
||||
'Find good funnels to track (onboarding or checkout)',
|
||||
'Conversions (how many clicks your hero CTA)',
|
||||
'What did the user do after clicking the CTA',
|
||||
]}
|
||||
/>
|
||||
<Text>
|
||||
- The first meaningful action (create something, send something, buy
|
||||
something)
|
||||
</Text>
|
||||
<Text>- Return visits</Text>
|
||||
<Text>
|
||||
You don't need 50 events. Five good ones will tell you more than fifty
|
||||
random ones.
|
||||
Start small and incrementally add more events as you go is usually the
|
||||
best approach.
|
||||
</Text>
|
||||
<Text>
|
||||
If you're not sure whether something's worth tracking, just ask. I'm
|
||||
happy to look at your setup.
|
||||
If you're not sure whether something's worth tracking, or have any
|
||||
questions, just reply here.
|
||||
</Text>
|
||||
<Text>
|
||||
Best regards,
|
||||
<br />
|
||||
Carl
|
||||
</Text>
|
||||
<Text>Carl</Text>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user