shipd 0.1.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/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1366 -0
- package/docs-template/README.md +255 -0
- package/docs-template/[slug]/[subslug]/page.tsx +1242 -0
- package/docs-template/[slug]/page.tsx +422 -0
- package/docs-template/api/page.tsx +47 -0
- package/docs-template/components/docs/docs-category-page.tsx +162 -0
- package/docs-template/components/docs/docs-code-card.tsx +135 -0
- package/docs-template/components/docs/docs-header.tsx +69 -0
- package/docs-template/components/docs/docs-nav.ts +95 -0
- package/docs-template/components/docs/docs-sidebar.tsx +112 -0
- package/docs-template/components/docs/docs-toc.tsx +38 -0
- package/docs-template/components/ui/badge.tsx +47 -0
- package/docs-template/components/ui/button.tsx +60 -0
- package/docs-template/components/ui/card.tsx +93 -0
- package/docs-template/components/ui/sheet.tsx +140 -0
- package/docs-template/documentation/page.tsx +80 -0
- package/docs-template/layout.tsx +27 -0
- package/docs-template/lib/utils.ts +7 -0
- package/docs-template/page.tsx +360 -0
- package/package.json +66 -0
- package/template/.env.example +45 -0
- package/template/README.md +239 -0
- package/template/app/api/auth/[...all]/route.ts +4 -0
- package/template/app/api/chat/route.ts +16 -0
- package/template/app/api/subscription/route.ts +25 -0
- package/template/app/api/upload-image/route.ts +64 -0
- package/template/app/blog/[slug]/page.tsx +314 -0
- package/template/app/blog/page.tsx +107 -0
- package/template/app/dashboard/_components/chart-interactive.tsx +289 -0
- package/template/app/dashboard/_components/chatbot.tsx +39 -0
- package/template/app/dashboard/_components/mode-toggle.tsx +46 -0
- package/template/app/dashboard/_components/navbar.tsx +84 -0
- package/template/app/dashboard/_components/section-cards.tsx +102 -0
- package/template/app/dashboard/_components/sidebar.tsx +90 -0
- package/template/app/dashboard/_components/subscribe-button.tsx +49 -0
- package/template/app/dashboard/billing/page.tsx +277 -0
- package/template/app/dashboard/chat/page.tsx +73 -0
- package/template/app/dashboard/cli/page.tsx +260 -0
- package/template/app/dashboard/layout.tsx +24 -0
- package/template/app/dashboard/page.tsx +216 -0
- package/template/app/dashboard/payment/_components/manage-subscription.tsx +22 -0
- package/template/app/dashboard/payment/page.tsx +126 -0
- package/template/app/dashboard/settings/page.tsx +613 -0
- package/template/app/dashboard/upload/page.tsx +324 -0
- package/template/app/error.tsx +78 -0
- package/template/app/favicon.ico +0 -0
- package/template/app/globals.css +126 -0
- package/template/app/layout.tsx +135 -0
- package/template/app/not-found.tsx +45 -0
- package/template/app/page.tsx +28 -0
- package/template/app/pricing/_component/pricing-table.tsx +276 -0
- package/template/app/pricing/page.tsx +23 -0
- package/template/app/privacy-policy/page.tsx +280 -0
- package/template/app/robots.txt +12 -0
- package/template/app/sign-in/page.tsx +228 -0
- package/template/app/sign-up/page.tsx +243 -0
- package/template/app/sitemap.ts +62 -0
- package/template/app/success/page.tsx +123 -0
- package/template/app/terms-of-service/page.tsx +212 -0
- package/template/auth-schema.ts +47 -0
- package/template/components/homepage/cli-workflow-section.tsx +138 -0
- package/template/components/homepage/features-section.tsx +150 -0
- package/template/components/homepage/footer.tsx +53 -0
- package/template/components/homepage/hero-section.tsx +112 -0
- package/template/components/homepage/integrations.tsx +124 -0
- package/template/components/homepage/navigation.tsx +116 -0
- package/template/components/homepage/news-section.tsx +82 -0
- package/template/components/homepage/testimonials-section.tsx +34 -0
- package/template/components/logos/BetterAuth.tsx +21 -0
- package/template/components/logos/NeonPostgres.tsx +41 -0
- package/template/components/logos/Nextjs.tsx +72 -0
- package/template/components/logos/Polar.tsx +7 -0
- package/template/components/logos/TailwindCSS.tsx +27 -0
- package/template/components/logos/index.ts +6 -0
- package/template/components/logos/shadcnui.tsx +8 -0
- package/template/components/provider.tsx +8 -0
- package/template/components/ui/avatar.tsx +53 -0
- package/template/components/ui/badge.tsx +46 -0
- package/template/components/ui/button.tsx +59 -0
- package/template/components/ui/card.tsx +92 -0
- package/template/components/ui/chart.tsx +353 -0
- package/template/components/ui/checkbox.tsx +32 -0
- package/template/components/ui/dialog.tsx +135 -0
- package/template/components/ui/dropdown-menu.tsx +257 -0
- package/template/components/ui/form.tsx +167 -0
- package/template/components/ui/input.tsx +21 -0
- package/template/components/ui/label.tsx +24 -0
- package/template/components/ui/progress.tsx +31 -0
- package/template/components/ui/resizable.tsx +56 -0
- package/template/components/ui/select.tsx +185 -0
- package/template/components/ui/separator.tsx +28 -0
- package/template/components/ui/sheet.tsx +139 -0
- package/template/components/ui/skeleton.tsx +13 -0
- package/template/components/ui/sonner.tsx +25 -0
- package/template/components/ui/switch.tsx +31 -0
- package/template/components/ui/tabs.tsx +66 -0
- package/template/components/ui/textarea.tsx +18 -0
- package/template/components/ui/toggle-group.tsx +73 -0
- package/template/components/ui/toggle.tsx +47 -0
- package/template/components/ui/tooltip.tsx +61 -0
- package/template/components/user-profile.tsx +139 -0
- package/template/components.json +21 -0
- package/template/db/drizzle.ts +14 -0
- package/template/db/migrations/0000_worried_rawhide_kid.sql +77 -0
- package/template/db/migrations/meta/0000_snapshot.json +494 -0
- package/template/db/migrations/meta/_journal.json +13 -0
- package/template/db/schema.ts +85 -0
- package/template/drizzle.config.ts +13 -0
- package/template/emails/components/layout.tsx +181 -0
- package/template/emails/password-reset.tsx +67 -0
- package/template/emails/payment-failed.tsx +167 -0
- package/template/emails/subscription-confirmation.tsx +129 -0
- package/template/emails/welcome.tsx +100 -0
- package/template/eslint.config.mjs +16 -0
- package/template/hooks/use-mobile.ts +21 -0
- package/template/lib/auth-client.ts +8 -0
- package/template/lib/auth.ts +276 -0
- package/template/lib/email.ts +118 -0
- package/template/lib/polar-products.ts +49 -0
- package/template/lib/subscription.ts +148 -0
- package/template/lib/upload-image.ts +28 -0
- package/template/lib/utils.ts +6 -0
- package/template/middleware.ts +30 -0
- package/template/next-env.d.ts +5 -0
- package/template/next.config.ts +27 -0
- package/template/package.json +99 -0
- package/template/postcss.config.mjs +5 -0
- package/template/public/add.png +0 -0
- package/template/public/favicon.svg +4 -0
- package/template/public/file.svg +1 -0
- package/template/public/globe.svg +1 -0
- package/template/public/iphone.png +0 -0
- package/template/public/logo.png +0 -0
- package/template/public/next.svg +1 -0
- package/template/public/polar-sh.svg +1 -0
- package/template/public/shadcn-ui.svg +1 -0
- package/template/public/site.webmanifest +21 -0
- package/template/public/vercel.svg +1 -0
- package/template/public/window.svg +1 -0
- package/template/tailwind.config.ts +89 -0
- package/template/template.config.json +138 -0
- package/template/tsconfig.json +27 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Html,
|
|
3
|
+
Head,
|
|
4
|
+
Preview,
|
|
5
|
+
Body,
|
|
6
|
+
Container,
|
|
7
|
+
Section,
|
|
8
|
+
Text,
|
|
9
|
+
Link,
|
|
10
|
+
Hr,
|
|
11
|
+
} from '@react-email/components';
|
|
12
|
+
|
|
13
|
+
interface EmailLayoutProps {
|
|
14
|
+
preview: string;
|
|
15
|
+
children: React.ReactNode;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function EmailLayout({ preview, children }: EmailLayoutProps) {
|
|
19
|
+
return (
|
|
20
|
+
<Html>
|
|
21
|
+
<Head />
|
|
22
|
+
<Preview>{preview}</Preview>
|
|
23
|
+
<Body style={main}>
|
|
24
|
+
<Container style={container}>
|
|
25
|
+
{/* Header */}
|
|
26
|
+
<Section style={header}>
|
|
27
|
+
<Text style={logo}>SaaS Scaffold</Text>
|
|
28
|
+
</Section>
|
|
29
|
+
|
|
30
|
+
{/* Main Content */}
|
|
31
|
+
<Section style={content}>
|
|
32
|
+
{children}
|
|
33
|
+
</Section>
|
|
34
|
+
|
|
35
|
+
{/* Footer */}
|
|
36
|
+
<Hr style={hr} />
|
|
37
|
+
<Section style={footer}>
|
|
38
|
+
<Text style={footerText}>
|
|
39
|
+
© {new Date().getFullYear()} SaaS Scaffold. All rights reserved.
|
|
40
|
+
</Text>
|
|
41
|
+
<Text style={footerLinks}>
|
|
42
|
+
<Link href="https://saas-scaffold.com" style={link}>
|
|
43
|
+
Website
|
|
44
|
+
</Link>
|
|
45
|
+
{' • '}
|
|
46
|
+
<Link href="https://saas-scaffold.com/docs" style={link}>
|
|
47
|
+
Docs
|
|
48
|
+
</Link>
|
|
49
|
+
{' • '}
|
|
50
|
+
<Link href="https://saas-scaffold.com/support" style={link}>
|
|
51
|
+
Support
|
|
52
|
+
</Link>
|
|
53
|
+
</Text>
|
|
54
|
+
<Text style={footerText}>
|
|
55
|
+
You received this email because you have an account with SaaS Scaffold.
|
|
56
|
+
</Text>
|
|
57
|
+
</Section>
|
|
58
|
+
</Container>
|
|
59
|
+
</Body>
|
|
60
|
+
</Html>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Styles
|
|
65
|
+
const main = {
|
|
66
|
+
backgroundColor: '#000000',
|
|
67
|
+
fontFamily:
|
|
68
|
+
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif',
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const container = {
|
|
72
|
+
backgroundColor: '#0a0a0a',
|
|
73
|
+
margin: '0 auto',
|
|
74
|
+
padding: '20px 0 48px',
|
|
75
|
+
marginBottom: '64px',
|
|
76
|
+
maxWidth: '600px',
|
|
77
|
+
border: '1px solid #2a2a2a',
|
|
78
|
+
borderRadius: '8px',
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const header = {
|
|
82
|
+
padding: '32px 48px',
|
|
83
|
+
backgroundColor: '#000000',
|
|
84
|
+
borderBottom: '1px solid #2a2a2a',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const logo = {
|
|
88
|
+
fontSize: '24px',
|
|
89
|
+
fontWeight: 'bold',
|
|
90
|
+
color: '#ffffff',
|
|
91
|
+
margin: 0,
|
|
92
|
+
background: 'linear-gradient(135deg, #ff5722 0%, #d84315 100%)',
|
|
93
|
+
WebkitBackgroundClip: 'text',
|
|
94
|
+
WebkitTextFillColor: 'transparent',
|
|
95
|
+
backgroundClip: 'text',
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const content = {
|
|
99
|
+
padding: '48px 48px 24px',
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const hr = {
|
|
103
|
+
borderColor: '#2a2a2a',
|
|
104
|
+
margin: '32px 48px',
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const footer = {
|
|
108
|
+
padding: '0 48px 32px',
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const footerText = {
|
|
112
|
+
fontSize: '12px',
|
|
113
|
+
lineHeight: '16px',
|
|
114
|
+
color: '#666666',
|
|
115
|
+
margin: '8px 0',
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const footerLinks = {
|
|
119
|
+
fontSize: '12px',
|
|
120
|
+
lineHeight: '16px',
|
|
121
|
+
color: '#666666',
|
|
122
|
+
margin: '16px 0 8px',
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const link = {
|
|
126
|
+
color: '#ff5722',
|
|
127
|
+
textDecoration: 'none',
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Reusable component styles
|
|
131
|
+
export const styles = {
|
|
132
|
+
h1: {
|
|
133
|
+
fontSize: '32px',
|
|
134
|
+
fontWeight: 'bold',
|
|
135
|
+
color: '#ffffff',
|
|
136
|
+
margin: '0 0 16px',
|
|
137
|
+
},
|
|
138
|
+
h2: {
|
|
139
|
+
fontSize: '24px',
|
|
140
|
+
fontWeight: 'bold',
|
|
141
|
+
color: '#ffffff',
|
|
142
|
+
margin: '32px 0 16px',
|
|
143
|
+
},
|
|
144
|
+
p: {
|
|
145
|
+
fontSize: '16px',
|
|
146
|
+
lineHeight: '24px',
|
|
147
|
+
color: '#cccccc',
|
|
148
|
+
margin: '16px 0',
|
|
149
|
+
},
|
|
150
|
+
button: {
|
|
151
|
+
backgroundColor: '#ff5722',
|
|
152
|
+
borderRadius: '6px',
|
|
153
|
+
color: '#ffffff',
|
|
154
|
+
fontSize: '16px',
|
|
155
|
+
fontWeight: '600',
|
|
156
|
+
textDecoration: 'none',
|
|
157
|
+
textAlign: 'center' as const,
|
|
158
|
+
display: 'inline-block',
|
|
159
|
+
padding: '12px 32px',
|
|
160
|
+
margin: '24px 0',
|
|
161
|
+
},
|
|
162
|
+
code: {
|
|
163
|
+
backgroundColor: '#1a1a1a',
|
|
164
|
+
border: '1px solid #2a2a2a',
|
|
165
|
+
borderRadius: '4px',
|
|
166
|
+
color: '#ff5722',
|
|
167
|
+
fontFamily: 'monospace',
|
|
168
|
+
fontSize: '14px',
|
|
169
|
+
padding: '2px 6px',
|
|
170
|
+
},
|
|
171
|
+
codeBlock: {
|
|
172
|
+
backgroundColor: '#1a1a1a',
|
|
173
|
+
border: '1px solid #2a2a2a',
|
|
174
|
+
borderRadius: '6px',
|
|
175
|
+
color: '#ffffff',
|
|
176
|
+
fontFamily: 'monospace',
|
|
177
|
+
fontSize: '14px',
|
|
178
|
+
padding: '16px',
|
|
179
|
+
margin: '16px 0',
|
|
180
|
+
},
|
|
181
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Text, Link, Section } from '@react-email/components';
|
|
2
|
+
import { EmailLayout, styles } from './components/layout';
|
|
3
|
+
|
|
4
|
+
interface PasswordResetEmailProps {
|
|
5
|
+
resetUrl: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function PasswordResetEmail({ resetUrl }: PasswordResetEmailProps) {
|
|
9
|
+
return (
|
|
10
|
+
<EmailLayout preview="Reset your SaaS Scaffold password">
|
|
11
|
+
<Text style={styles.h1}>Reset your password</Text>
|
|
12
|
+
|
|
13
|
+
<Text style={styles.p}>
|
|
14
|
+
We received a request to reset your password for your SaaS Scaffold account.
|
|
15
|
+
</Text>
|
|
16
|
+
|
|
17
|
+
<Text style={styles.p}>
|
|
18
|
+
Click the button below to choose a new password. This link will expire in 1 hour for security reasons.
|
|
19
|
+
</Text>
|
|
20
|
+
|
|
21
|
+
<Link href={resetUrl} style={styles.button}>
|
|
22
|
+
Reset Password
|
|
23
|
+
</Link>
|
|
24
|
+
|
|
25
|
+
<Section style={warningBox}>
|
|
26
|
+
<Text style={warningText}>
|
|
27
|
+
<strong>Security Tip:</strong> If you didn't request this password reset, you can safely ignore this email. Your password will remain unchanged.
|
|
28
|
+
</Text>
|
|
29
|
+
</Section>
|
|
30
|
+
|
|
31
|
+
<Text style={styles.p}>
|
|
32
|
+
For your security, this password reset link can only be used once and will expire in 60 minutes.
|
|
33
|
+
</Text>
|
|
34
|
+
|
|
35
|
+
<Text style={{ ...styles.p, marginTop: '32px' }}>
|
|
36
|
+
If you're having trouble clicking the button, copy and paste this URL into your browser:
|
|
37
|
+
</Text>
|
|
38
|
+
|
|
39
|
+
<Section style={styles.codeBlock}>
|
|
40
|
+
{resetUrl}
|
|
41
|
+
</Section>
|
|
42
|
+
|
|
43
|
+
<Text style={styles.p}>
|
|
44
|
+
Thanks,<br />
|
|
45
|
+
The SaaS Scaffold Team
|
|
46
|
+
</Text>
|
|
47
|
+
</EmailLayout>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Additional styles
|
|
52
|
+
const warningBox = {
|
|
53
|
+
backgroundColor: '#2a1a1a',
|
|
54
|
+
border: '1px solid #ff5722',
|
|
55
|
+
borderRadius: '6px',
|
|
56
|
+
padding: '16px',
|
|
57
|
+
margin: '24px 0',
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const warningText = {
|
|
61
|
+
fontSize: '14px',
|
|
62
|
+
lineHeight: '20px',
|
|
63
|
+
color: '#ff9a80',
|
|
64
|
+
margin: 0,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default PasswordResetEmail;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { Text, Link, Section } from '@react-email/components';
|
|
2
|
+
import { EmailLayout, styles } from './components/layout';
|
|
3
|
+
|
|
4
|
+
interface PaymentFailedEmailProps {
|
|
5
|
+
planName: string;
|
|
6
|
+
retryDate: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function PaymentFailedEmail({
|
|
10
|
+
planName,
|
|
11
|
+
retryDate,
|
|
12
|
+
}: PaymentFailedEmailProps) {
|
|
13
|
+
return (
|
|
14
|
+
<EmailLayout preview="Payment failed - Action required">
|
|
15
|
+
<Text style={styles.h1}>Payment Failed</Text>
|
|
16
|
+
|
|
17
|
+
<Section style={alertBox}>
|
|
18
|
+
<Text style={alertText}>
|
|
19
|
+
<strong>Action Required:</strong> We couldn't process your payment for the {planName} subscription.
|
|
20
|
+
</Text>
|
|
21
|
+
</Section>
|
|
22
|
+
|
|
23
|
+
<Text style={styles.p}>
|
|
24
|
+
We attempted to charge your payment method on file, but the payment was declined. This can happen for several reasons:
|
|
25
|
+
</Text>
|
|
26
|
+
|
|
27
|
+
<ul style={list}>
|
|
28
|
+
<li style={listItem}>
|
|
29
|
+
<Text style={listText}>Insufficient funds</Text>
|
|
30
|
+
</li>
|
|
31
|
+
<li style={listItem}>
|
|
32
|
+
<Text style={listText}>Expired card</Text>
|
|
33
|
+
</li>
|
|
34
|
+
<li style={listItem}>
|
|
35
|
+
<Text style={listText}>Card blocked or frozen</Text>
|
|
36
|
+
</li>
|
|
37
|
+
<li style={listItem}>
|
|
38
|
+
<Text style={listText}>Incorrect billing information</Text>
|
|
39
|
+
</li>
|
|
40
|
+
</ul>
|
|
41
|
+
|
|
42
|
+
<Text style={styles.h2}>What Happens Next?</Text>
|
|
43
|
+
|
|
44
|
+
<Section style={infoBox}>
|
|
45
|
+
<table style={table}>
|
|
46
|
+
<tbody>
|
|
47
|
+
<tr>
|
|
48
|
+
<td style={labelCell}>Next retry:</td>
|
|
49
|
+
<td style={valueCell}>{retryDate}</td>
|
|
50
|
+
</tr>
|
|
51
|
+
<tr>
|
|
52
|
+
<td style={labelCell}>Status:</td>
|
|
53
|
+
<td style={valueCell}>Access maintained until retry</td>
|
|
54
|
+
</tr>
|
|
55
|
+
</tbody>
|
|
56
|
+
</table>
|
|
57
|
+
</Section>
|
|
58
|
+
|
|
59
|
+
<Text style={styles.p}>
|
|
60
|
+
Your access to SaaS Scaffold will remain active until our next retry attempt. To avoid any interruption, please update your payment method as soon as possible.
|
|
61
|
+
</Text>
|
|
62
|
+
|
|
63
|
+
<Link href="https://saas-scaffold.com/dashboard/billing" style={styles.button}>
|
|
64
|
+
Update Payment Method
|
|
65
|
+
</Link>
|
|
66
|
+
|
|
67
|
+
<Text style={styles.p}>
|
|
68
|
+
If the payment issue isn't resolved by the retry date, your subscription will be automatically canceled and access will be revoked.
|
|
69
|
+
</Text>
|
|
70
|
+
|
|
71
|
+
<Section style={supportBox}>
|
|
72
|
+
<Text style={supportText}>
|
|
73
|
+
Need help? Contact our support team at{' '}
|
|
74
|
+
<Link href="mailto:support@saas-scaffold.com" style={supportLink}>
|
|
75
|
+
support@saas-scaffold.com
|
|
76
|
+
</Link>
|
|
77
|
+
</Text>
|
|
78
|
+
</Section>
|
|
79
|
+
|
|
80
|
+
<Text style={styles.p}>
|
|
81
|
+
Thanks,<br />
|
|
82
|
+
The SaaS Scaffold Team
|
|
83
|
+
</Text>
|
|
84
|
+
</EmailLayout>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Additional styles
|
|
89
|
+
const alertBox = {
|
|
90
|
+
backgroundColor: '#2a1a1a',
|
|
91
|
+
border: '1px solid #ff5722',
|
|
92
|
+
borderRadius: '6px',
|
|
93
|
+
padding: '16px',
|
|
94
|
+
margin: '0 0 24px',
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const alertText = {
|
|
98
|
+
fontSize: '14px',
|
|
99
|
+
lineHeight: '20px',
|
|
100
|
+
color: '#ff9a80',
|
|
101
|
+
margin: 0,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const list = {
|
|
105
|
+
margin: '16px 0',
|
|
106
|
+
padding: '0 0 0 20px',
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const listItem = {
|
|
110
|
+
margin: '8px 0',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const listText = {
|
|
114
|
+
fontSize: '14px',
|
|
115
|
+
lineHeight: '20px',
|
|
116
|
+
color: '#cccccc',
|
|
117
|
+
margin: 0,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const infoBox = {
|
|
121
|
+
backgroundColor: '#1a1a1a',
|
|
122
|
+
border: '1px solid #2a2a2a',
|
|
123
|
+
borderRadius: '6px',
|
|
124
|
+
padding: '16px',
|
|
125
|
+
margin: '16px 0',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const table = {
|
|
129
|
+
width: '100%',
|
|
130
|
+
borderCollapse: 'collapse' as const,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const labelCell = {
|
|
134
|
+
fontSize: '14px',
|
|
135
|
+
color: '#999999',
|
|
136
|
+
padding: '8px 0',
|
|
137
|
+
width: '140px',
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const valueCell = {
|
|
141
|
+
fontSize: '14px',
|
|
142
|
+
color: '#ffffff',
|
|
143
|
+
fontWeight: '600',
|
|
144
|
+
padding: '8px 0',
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
const supportBox = {
|
|
148
|
+
backgroundColor: '#1a1a1a',
|
|
149
|
+
border: '1px solid #2a2a2a',
|
|
150
|
+
borderRadius: '6px',
|
|
151
|
+
padding: '16px',
|
|
152
|
+
margin: '24px 0',
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const supportText = {
|
|
156
|
+
fontSize: '14px',
|
|
157
|
+
lineHeight: '20px',
|
|
158
|
+
color: '#cccccc',
|
|
159
|
+
margin: 0,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const supportLink = {
|
|
163
|
+
color: '#ff5722',
|
|
164
|
+
textDecoration: 'none',
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export default PaymentFailedEmail;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Text, Link, Section } from '@react-email/components';
|
|
2
|
+
import { EmailLayout, styles } from './components/layout';
|
|
3
|
+
|
|
4
|
+
interface SubscriptionConfirmationEmailProps {
|
|
5
|
+
planName: string;
|
|
6
|
+
amount: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function SubscriptionConfirmationEmail({
|
|
10
|
+
planName,
|
|
11
|
+
amount,
|
|
12
|
+
}: SubscriptionConfirmationEmailProps) {
|
|
13
|
+
return (
|
|
14
|
+
<EmailLayout preview={`Your ${planName} subscription is confirmed`}>
|
|
15
|
+
<Text style={styles.h1}>Subscription Confirmed! 🎉</Text>
|
|
16
|
+
|
|
17
|
+
<Text style={styles.p}>
|
|
18
|
+
Thank you for subscribing to SaaS Scaffold! Your subscription has been successfully activated.
|
|
19
|
+
</Text>
|
|
20
|
+
|
|
21
|
+
<Section style={summaryBox}>
|
|
22
|
+
<Text style={summaryTitle}>Subscription Summary</Text>
|
|
23
|
+
<table style={table}>
|
|
24
|
+
<tbody>
|
|
25
|
+
<tr>
|
|
26
|
+
<td style={labelCell}>Plan:</td>
|
|
27
|
+
<td style={valueCell}>{planName}</td>
|
|
28
|
+
</tr>
|
|
29
|
+
<tr>
|
|
30
|
+
<td style={labelCell}>Amount:</td>
|
|
31
|
+
<td style={valueCell}>{amount}</td>
|
|
32
|
+
</tr>
|
|
33
|
+
<tr>
|
|
34
|
+
<td style={labelCell}>Billing:</td>
|
|
35
|
+
<td style={valueCell}>Monthly (auto-renews)</td>
|
|
36
|
+
</tr>
|
|
37
|
+
</tbody>
|
|
38
|
+
</table>
|
|
39
|
+
</Section>
|
|
40
|
+
|
|
41
|
+
<Text style={styles.h2}>What's Next?</Text>
|
|
42
|
+
|
|
43
|
+
<Text style={styles.p}>
|
|
44
|
+
You now have access to all premium features:
|
|
45
|
+
</Text>
|
|
46
|
+
|
|
47
|
+
<ul style={list}>
|
|
48
|
+
<li style={listItem}>
|
|
49
|
+
<Text style={listText}>Generate unlimited projects with our CLI</Text>
|
|
50
|
+
</li>
|
|
51
|
+
<li style={listItem}>
|
|
52
|
+
<Text style={listText}>Access to all premium templates</Text>
|
|
53
|
+
</li>
|
|
54
|
+
<li style={listItem}>
|
|
55
|
+
<Text style={listText}>Priority support and updates</Text>
|
|
56
|
+
</li>
|
|
57
|
+
<li style={listItem}>
|
|
58
|
+
<Text style={listText}>Exclusive community access</Text>
|
|
59
|
+
</li>
|
|
60
|
+
</ul>
|
|
61
|
+
|
|
62
|
+
<Link href="https://saas-scaffold.com/dashboard" style={styles.button}>
|
|
63
|
+
Go to Dashboard
|
|
64
|
+
</Link>
|
|
65
|
+
|
|
66
|
+
<Text style={styles.p}>
|
|
67
|
+
You can manage your subscription, update payment methods, or view invoices anytime from your dashboard.
|
|
68
|
+
</Text>
|
|
69
|
+
|
|
70
|
+
<Text style={styles.p}>
|
|
71
|
+
Thanks for your support!<br />
|
|
72
|
+
The SaaS Scaffold Team
|
|
73
|
+
</Text>
|
|
74
|
+
</EmailLayout>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Additional styles
|
|
79
|
+
const summaryBox = {
|
|
80
|
+
backgroundColor: '#1a1a1a',
|
|
81
|
+
border: '1px solid #2a2a2a',
|
|
82
|
+
borderRadius: '6px',
|
|
83
|
+
padding: '24px',
|
|
84
|
+
margin: '24px 0',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const summaryTitle = {
|
|
88
|
+
fontSize: '18px',
|
|
89
|
+
fontWeight: '600',
|
|
90
|
+
color: '#ffffff',
|
|
91
|
+
margin: '0 0 16px',
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const table = {
|
|
95
|
+
width: '100%',
|
|
96
|
+
borderCollapse: 'collapse' as const,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const labelCell = {
|
|
100
|
+
fontSize: '14px',
|
|
101
|
+
color: '#999999',
|
|
102
|
+
padding: '8px 0',
|
|
103
|
+
width: '100px',
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const valueCell = {
|
|
107
|
+
fontSize: '14px',
|
|
108
|
+
color: '#ffffff',
|
|
109
|
+
fontWeight: '600',
|
|
110
|
+
padding: '8px 0',
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const list = {
|
|
114
|
+
margin: '16px 0',
|
|
115
|
+
padding: '0 0 0 20px',
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const listItem = {
|
|
119
|
+
margin: '12px 0',
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const listText = {
|
|
123
|
+
fontSize: '14px',
|
|
124
|
+
lineHeight: '20px',
|
|
125
|
+
color: '#cccccc',
|
|
126
|
+
margin: 0,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export default SubscriptionConfirmationEmail;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Text, Link, Section } from '@react-email/components';
|
|
2
|
+
import { EmailLayout, styles } from './components/layout';
|
|
3
|
+
|
|
4
|
+
interface WelcomeEmailProps {
|
|
5
|
+
userName?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function WelcomeEmail({ userName = 'there' }: WelcomeEmailProps) {
|
|
9
|
+
return (
|
|
10
|
+
<EmailLayout preview="Welcome to SaaS Scaffold! Let's get you started.">
|
|
11
|
+
<Text style={styles.h1}>Welcome to SaaS Scaffold! 👋</Text>
|
|
12
|
+
|
|
13
|
+
<Text style={styles.p}>
|
|
14
|
+
Hi {userName},
|
|
15
|
+
</Text>
|
|
16
|
+
|
|
17
|
+
<Text style={styles.p}>
|
|
18
|
+
Thanks for joining SaaS Scaffold! We're excited to help you build and ship your SaaS product faster than ever.
|
|
19
|
+
</Text>
|
|
20
|
+
|
|
21
|
+
<Section style={cardContainer}>
|
|
22
|
+
<Text style={cardTitle}>What's included in your subscription:</Text>
|
|
23
|
+
<ul style={list}>
|
|
24
|
+
<li style={listItem}>
|
|
25
|
+
<Text style={listText}><strong>Premium Templates</strong> - Production-ready Next.js templates</Text>
|
|
26
|
+
</li>
|
|
27
|
+
<li style={listItem}>
|
|
28
|
+
<Text style={listText}><strong>CLI Tool</strong> - Generate projects in seconds</Text>
|
|
29
|
+
</li>
|
|
30
|
+
<li style={listItem}>
|
|
31
|
+
<Text style={listText}><strong>Authentication</strong> - Better Auth with Polar.sh integration</Text>
|
|
32
|
+
</li>
|
|
33
|
+
<li style={listItem}>
|
|
34
|
+
<Text style={listText}><strong>Billing</strong> - Subscriptions and payments pre-configured</Text>
|
|
35
|
+
</li>
|
|
36
|
+
<li style={listItem}>
|
|
37
|
+
<Text style={listText}><strong>Priority Support</strong> - Get help when you need it</Text>
|
|
38
|
+
</li>
|
|
39
|
+
</ul>
|
|
40
|
+
</Section>
|
|
41
|
+
|
|
42
|
+
<Text style={styles.h2}>Get Started</Text>
|
|
43
|
+
|
|
44
|
+
<Text style={styles.p}>
|
|
45
|
+
Ready to create your first project? Run this command in your terminal:
|
|
46
|
+
</Text>
|
|
47
|
+
|
|
48
|
+
<Section style={styles.codeBlock}>
|
|
49
|
+
npx saas-scaffold init my-project
|
|
50
|
+
</Section>
|
|
51
|
+
|
|
52
|
+
<Link href="https://saas-scaffold.com/docs/getting-started" style={styles.button}>
|
|
53
|
+
View Documentation
|
|
54
|
+
</Link>
|
|
55
|
+
|
|
56
|
+
<Text style={styles.p}>
|
|
57
|
+
If you have any questions or need help getting started, don't hesitate to reach out to our support team.
|
|
58
|
+
</Text>
|
|
59
|
+
|
|
60
|
+
<Text style={styles.p}>
|
|
61
|
+
Happy building!<br />
|
|
62
|
+
The SaaS Scaffold Team
|
|
63
|
+
</Text>
|
|
64
|
+
</EmailLayout>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Additional styles
|
|
69
|
+
const cardContainer = {
|
|
70
|
+
backgroundColor: '#1a1a1a',
|
|
71
|
+
border: '1px solid #2a2a2a',
|
|
72
|
+
borderRadius: '6px',
|
|
73
|
+
padding: '24px',
|
|
74
|
+
margin: '24px 0',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const cardTitle = {
|
|
78
|
+
fontSize: '18px',
|
|
79
|
+
fontWeight: '600',
|
|
80
|
+
color: '#ffffff',
|
|
81
|
+
margin: '0 0 16px',
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const list = {
|
|
85
|
+
margin: '0',
|
|
86
|
+
padding: '0 0 0 20px',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const listItem = {
|
|
90
|
+
margin: '12px 0',
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const listText = {
|
|
94
|
+
fontSize: '14px',
|
|
95
|
+
lineHeight: '20px',
|
|
96
|
+
color: '#cccccc',
|
|
97
|
+
margin: 0,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export default WelcomeEmail;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { dirname } from "path";
|
|
2
|
+
import { fileURLToPath } from "url";
|
|
3
|
+
import { FlatCompat } from "@eslint/eslintrc";
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
|
|
8
|
+
const compat = new FlatCompat({
|
|
9
|
+
baseDirectory: __dirname,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const eslintConfig = [
|
|
13
|
+
...compat.extends("next/core-web-vitals", "next/typescript"),
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export default eslintConfig;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768;
|
|
4
|
+
|
|
5
|
+
export function useIsMobile() {
|
|
6
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
|
|
7
|
+
undefined,
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
12
|
+
const onChange = () => {
|
|
13
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
14
|
+
};
|
|
15
|
+
mql.addEventListener("change", onChange);
|
|
16
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
17
|
+
return () => mql.removeEventListener("change", onChange);
|
|
18
|
+
}, []);
|
|
19
|
+
|
|
20
|
+
return !!isMobile;
|
|
21
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createAuthClient } from "better-auth/react";
|
|
2
|
+
import { organizationClient } from "better-auth/client/plugins";
|
|
3
|
+
import { polarClient } from "@polar-sh/better-auth";
|
|
4
|
+
|
|
5
|
+
export const authClient = createAuthClient({
|
|
6
|
+
baseURL: process.env.NEXT_PUBLIC_APP_URL,
|
|
7
|
+
plugins: [organizationClient(), polarClient()],
|
|
8
|
+
});
|