shipd 0.1.2 → 0.1.4

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 (115) hide show
  1. package/base-package/app/globals.css +126 -0
  2. package/base-package/app/layout.tsx +53 -0
  3. package/base-package/app/page.tsx +15 -0
  4. package/base-package/base.config.json +57 -0
  5. package/base-package/components/ui/avatar.tsx +53 -0
  6. package/base-package/components/ui/badge.tsx +46 -0
  7. package/base-package/components/ui/button.tsx +59 -0
  8. package/base-package/components/ui/card.tsx +92 -0
  9. package/base-package/components/ui/chart.tsx +353 -0
  10. package/base-package/components/ui/checkbox.tsx +32 -0
  11. package/base-package/components/ui/dialog.tsx +135 -0
  12. package/base-package/components/ui/dropdown-menu.tsx +257 -0
  13. package/base-package/components/ui/form.tsx +167 -0
  14. package/base-package/components/ui/input.tsx +21 -0
  15. package/base-package/components/ui/label.tsx +24 -0
  16. package/base-package/components/ui/progress.tsx +31 -0
  17. package/base-package/components/ui/resizable.tsx +56 -0
  18. package/base-package/components/ui/select.tsx +185 -0
  19. package/base-package/components/ui/separator.tsx +28 -0
  20. package/base-package/components/ui/sheet.tsx +139 -0
  21. package/base-package/components/ui/skeleton.tsx +13 -0
  22. package/base-package/components/ui/sonner.tsx +25 -0
  23. package/base-package/components/ui/switch.tsx +31 -0
  24. package/base-package/components/ui/tabs.tsx +66 -0
  25. package/base-package/components/ui/textarea.tsx +18 -0
  26. package/base-package/components/ui/toggle-group.tsx +73 -0
  27. package/base-package/components/ui/toggle.tsx +47 -0
  28. package/base-package/components/ui/tooltip.tsx +61 -0
  29. package/base-package/components.json +21 -0
  30. package/base-package/eslint.config.mjs +16 -0
  31. package/base-package/lib/utils.ts +6 -0
  32. package/base-package/middleware.ts +12 -0
  33. package/base-package/next.config.ts +27 -0
  34. package/base-package/package.json +49 -0
  35. package/base-package/postcss.config.mjs +5 -0
  36. package/base-package/public/favicon.svg +4 -0
  37. package/base-package/tailwind.config.ts +89 -0
  38. package/base-package/tsconfig.json +27 -0
  39. package/dist/index.js +1862 -948
  40. package/features/ai-chat/README.md +258 -0
  41. package/features/ai-chat/app/api/chat/route.ts +16 -0
  42. package/features/ai-chat/app/dashboard/_components/chatbot.tsx +39 -0
  43. package/features/ai-chat/app/dashboard/chat/page.tsx +73 -0
  44. package/features/ai-chat/feature.config.json +22 -0
  45. package/features/analytics/README.md +308 -0
  46. package/features/analytics/feature.config.json +20 -0
  47. package/features/analytics/lib/posthog.ts +36 -0
  48. package/features/auth/README.md +336 -0
  49. package/features/auth/app/api/auth/[...all]/route.ts +4 -0
  50. package/features/auth/app/dashboard/layout.tsx +15 -0
  51. package/features/auth/app/dashboard/page.tsx +140 -0
  52. package/features/auth/app/sign-in/page.tsx +228 -0
  53. package/features/auth/app/sign-up/page.tsx +243 -0
  54. package/features/auth/auth-schema.ts +47 -0
  55. package/features/auth/components/auth/setup-instructions.tsx +123 -0
  56. package/features/auth/feature.config.json +33 -0
  57. package/features/auth/lib/auth-client.ts +8 -0
  58. package/features/auth/lib/auth.ts +295 -0
  59. package/features/auth/lib/email-stub.ts +55 -0
  60. package/features/auth/lib/email.ts +47 -0
  61. package/features/auth/middleware.patch.ts +43 -0
  62. package/features/database/README.md +256 -0
  63. package/features/database/db/drizzle.ts +48 -0
  64. package/features/database/db/schema.ts +21 -0
  65. package/features/database/drizzle.config.ts +13 -0
  66. package/features/database/feature.config.json +30 -0
  67. package/features/email/README.md +282 -0
  68. package/features/email/emails/components/layout.tsx +181 -0
  69. package/features/email/emails/password-reset.tsx +67 -0
  70. package/features/email/emails/payment-failed.tsx +167 -0
  71. package/features/email/emails/subscription-confirmation.tsx +129 -0
  72. package/features/email/emails/welcome.tsx +100 -0
  73. package/features/email/feature.config.json +22 -0
  74. package/features/email/lib/email.ts +118 -0
  75. package/features/file-upload/README.md +271 -0
  76. package/features/file-upload/app/api/upload-image/route.ts +64 -0
  77. package/features/file-upload/app/dashboard/upload/page.tsx +324 -0
  78. package/features/file-upload/feature.config.json +23 -0
  79. package/features/file-upload/lib/upload-image.ts +28 -0
  80. package/features/marketing-landing/README.md +266 -0
  81. package/features/marketing-landing/app/page.tsx +25 -0
  82. package/features/marketing-landing/components/homepage/cli-workflow-section.tsx +231 -0
  83. package/features/marketing-landing/components/homepage/features-section.tsx +152 -0
  84. package/features/marketing-landing/components/homepage/footer.tsx +53 -0
  85. package/features/marketing-landing/components/homepage/hero-section.tsx +112 -0
  86. package/features/marketing-landing/components/homepage/integrations.tsx +124 -0
  87. package/features/marketing-landing/components/homepage/navigation.tsx +116 -0
  88. package/features/marketing-landing/components/homepage/news-section.tsx +82 -0
  89. package/features/marketing-landing/components/homepage/pricing-section.tsx +98 -0
  90. package/features/marketing-landing/components/homepage/testimonials-section.tsx +34 -0
  91. package/features/marketing-landing/components/logos/BetterAuth.tsx +21 -0
  92. package/features/marketing-landing/components/logos/NeonPostgres.tsx +41 -0
  93. package/features/marketing-landing/components/logos/Nextjs.tsx +72 -0
  94. package/features/marketing-landing/components/logos/Polar.tsx +7 -0
  95. package/features/marketing-landing/components/logos/TailwindCSS.tsx +27 -0
  96. package/features/marketing-landing/components/logos/index.ts +6 -0
  97. package/features/marketing-landing/components/logos/shadcnui.tsx +8 -0
  98. package/features/marketing-landing/feature.config.json +23 -0
  99. package/features/payments/README.md +306 -0
  100. package/features/payments/app/api/subscription/route.ts +25 -0
  101. package/features/payments/app/dashboard/payment/_components/manage-subscription.tsx +22 -0
  102. package/features/payments/app/dashboard/payment/page.tsx +126 -0
  103. package/features/payments/app/success/page.tsx +123 -0
  104. package/features/payments/feature.config.json +31 -0
  105. package/features/payments/lib/polar-products.ts +49 -0
  106. package/features/payments/lib/subscription.ts +148 -0
  107. package/features/payments/payments-schema.ts +30 -0
  108. package/features/seo/README.md +244 -0
  109. package/features/seo/app/blog/[slug]/page.tsx +314 -0
  110. package/features/seo/app/blog/page.tsx +107 -0
  111. package/features/seo/app/robots.txt +13 -0
  112. package/features/seo/app/sitemap.ts +70 -0
  113. package/features/seo/feature.config.json +19 -0
  114. package/features/seo/lib/seo-utils.ts +163 -0
  115. package/package.json +3 -1
@@ -0,0 +1,48 @@
1
+ import postgres from "postgres";
2
+ import { drizzle } from "drizzle-orm/postgres-js";
3
+
4
+ // Lazy database connection - only connect when actually used
5
+ // This allows the app to start even without DATABASE_URL configured
6
+ let dbInstance: ReturnType<typeof drizzle> | null = null;
7
+ let connectionError: Error | null = null;
8
+
9
+ function getDb() {
10
+ if (dbInstance) {
11
+ return dbInstance;
12
+ }
13
+
14
+ if (connectionError) {
15
+ throw connectionError;
16
+ }
17
+
18
+ const connectionString = process.env.DATABASE_URL;
19
+ if (!connectionString) {
20
+ connectionError = new Error(
21
+ "Missing DATABASE_URL. Add it to your .env.local (or hosting provider env vars). " +
22
+ "You can use a Neon Postgres URL, Supabase, or any Postgres connection string."
23
+ );
24
+ throw connectionError;
25
+ }
26
+
27
+ try {
28
+ // Disable prepared statements for Supabase compatibility
29
+ const client = postgres(connectionString, { prepare: false });
30
+ dbInstance = drizzle(client);
31
+ return dbInstance;
32
+ } catch (error) {
33
+ connectionError = error instanceof Error ? error : new Error(String(error));
34
+ throw connectionError;
35
+ }
36
+ }
37
+
38
+ // Export a proxy that lazily initializes the database
39
+ export const db = new Proxy({} as ReturnType<typeof drizzle>, {
40
+ get(_target, prop) {
41
+ const db = getDb();
42
+ const value = db[prop as keyof ReturnType<typeof drizzle>];
43
+ if (typeof value === 'function') {
44
+ return value.bind(db);
45
+ }
46
+ return value;
47
+ }
48
+ });
@@ -0,0 +1,21 @@
1
+ import {
2
+ boolean,
3
+ integer,
4
+ pgTable,
5
+ text,
6
+ timestamp,
7
+ } from "drizzle-orm/pg-core";
8
+
9
+ /**
10
+ * Base Database Schema
11
+ *
12
+ * This is the base schema. Other modules (auth, payments) will add their tables.
13
+ * When appending modules, their schema files will be merged into this file.
14
+ */
15
+
16
+ // Auth tables will be added by the auth module
17
+ // Subscription table will be added by the payments module
18
+
19
+ // Base schema is empty - modules add their own tables
20
+ // This file serves as the entry point for schema merging
21
+
@@ -0,0 +1,13 @@
1
+ import { config } from 'dotenv';
2
+ import { defineConfig } from "drizzle-kit";
3
+
4
+ config({ path: '.env' });
5
+
6
+ export default defineConfig({
7
+ schema: "./db/schema.ts",
8
+ out: "./db/migrations",
9
+ dialect: "postgresql",
10
+ dbCredentials: {
11
+ url: process.env.DATABASE_URL!,
12
+ },
13
+ });
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "database",
3
+ "version": "1.0.0",
4
+ "description": "PostgreSQL database setup with Drizzle ORM",
5
+ "dependencies": {
6
+ "postgres": "^3.4.4",
7
+ "drizzle-orm": "^0.41.0"
8
+ },
9
+ "devDependencies": {
10
+ "drizzle-kit": "^0.31.0"
11
+ },
12
+ "envVars": [
13
+ "DATABASE_URL"
14
+ ],
15
+ "files": [
16
+ "drizzle.config.ts",
17
+ "db/drizzle.ts",
18
+ "db/schema.ts",
19
+ "db/migrations"
20
+ ],
21
+ "packageJsonScripts": {
22
+ "db:push": "drizzle-kit push",
23
+ "db:generate": "drizzle-kit generate",
24
+ "db:migrate": "drizzle-kit migrate",
25
+ "db:studio": "drizzle-kit studio"
26
+ },
27
+ "requires": [],
28
+ "conflicts": []
29
+ }
30
+
@@ -0,0 +1,282 @@
1
+ # Email Module - Integration Guide
2
+
3
+ **Module Version:** 1.0.0
4
+ **Last Updated:** 2025-12-22
5
+ **Standalone:** ✅ Yes (can work independently)
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ This module adds email sending functionality using Resend and React Email. It includes pre-built email templates for common use cases like welcome emails, password resets, subscription confirmations, and payment failures.
12
+
13
+ **Key Features:**
14
+ - Resend integration for email delivery
15
+ - React Email templates (beautiful, responsive emails)
16
+ - Pre-built email templates:
17
+ - Welcome email
18
+ - Password reset email
19
+ - Subscription confirmation email
20
+ - Payment failed email
21
+ - Graceful fallback (logs to console if not configured)
22
+ - Type-safe email sending
23
+
24
+ ---
25
+
26
+ ## Files Added
27
+
28
+ ### Utilities/Libraries
29
+ - `lib/email.ts` - Email sending utilities and helper functions
30
+
31
+ ### Email Templates
32
+ - `emails/welcome.tsx` - Welcome email template
33
+ - `emails/password-reset.tsx` - Password reset email template
34
+ - `emails/subscription-confirmation.tsx` - Subscription confirmation template
35
+ - `emails/payment-failed.tsx` - Payment failed notification template
36
+ - `emails/components/layout.tsx` - Shared email layout component
37
+
38
+ ---
39
+
40
+ ## Dependencies
41
+
42
+ ### Package Dependencies
43
+ The following packages will be added to `package.json`:
44
+ - `resend@^6.6.0` - Resend email service SDK
45
+ - `@react-email/components@^1.0.2` - React Email component library
46
+ - `@react-email/render@^2.0.0` - React Email rendering utilities
47
+
48
+ ### Required Modules
49
+ - None - Email module is standalone
50
+
51
+ ### Optional Integration
52
+ - **Auth Module** - Uses email for password resets (works without it, but emails won't send)
53
+ - **Payments Module** - Uses email for subscription confirmations (works without it, but emails won't send)
54
+
55
+ ---
56
+
57
+ ## Environment Variables
58
+
59
+ Add these to your `.env.local`:
60
+
61
+ ```env
62
+ # Email (Resend)
63
+ RESEND_API_KEY="re_xxxxxxxxxxxxx" # Your Resend API key
64
+ EMAIL_FROM="Your App <noreply@yourdomain.com>" # Sender email address
65
+ ```
66
+
67
+ **Required Variables:**
68
+ - `RESEND_API_KEY` - Your Resend API key (get from [resend.com](https://resend.com))
69
+
70
+ **Optional Variables:**
71
+ - `EMAIL_FROM` - Sender email address (defaults to "SaaS Scaffold <noreply@saas-scaffold.com>")
72
+
73
+ **Where to get them:**
74
+ - **Resend**: [resend.com](https://resend.com) - Free tier available (100 emails/day)
75
+ - Create an account and get your API key from the dashboard
76
+ - Verify your domain for production use
77
+
78
+ ---
79
+
80
+ ## Manual Integration Steps
81
+
82
+ If you're appending this module to an existing project, follow these steps:
83
+
84
+ ### Step 1: Install Dependencies
85
+
86
+ ```bash
87
+ npm install
88
+ # or
89
+ pnpm install
90
+ # or
91
+ yarn install
92
+ ```
93
+
94
+ The append command automatically adds missing dependencies to `package.json`, but you need to install them.
95
+
96
+ ### Step 2: Add Environment Variables
97
+
98
+ 1. Copy `.env.example` to `.env.local` (if not exists)
99
+ 2. Add `RESEND_API_KEY` with your Resend API key
100
+ 3. Optionally set `EMAIL_FROM` with your sender email
101
+
102
+ ### Step 3: Verify Installation
103
+
104
+ 1. Check that `lib/email.ts` exists
105
+ 2. Check that `emails/` directory exists with all templates
106
+ 3. Start your dev server: `npm run dev`
107
+
108
+ ### Step 4: Test Email Sending
109
+
110
+ The email module works gracefully without configuration:
111
+ - If `RESEND_API_KEY` is not set, emails are logged to console
112
+ - If `RESEND_API_KEY` is set, emails are sent via Resend
113
+
114
+ ---
115
+
116
+ ## Usage Examples
117
+
118
+ ### Sending a Welcome Email
119
+
120
+ ```typescript
121
+ import { sendWelcomeEmail } from "@/lib/email";
122
+
123
+ // In your sign-up handler
124
+ await sendWelcomeEmail(user.email, user.name);
125
+ ```
126
+
127
+ ### Sending a Password Reset Email
128
+
129
+ ```typescript
130
+ import { sendPasswordResetEmail } from "@/lib/email";
131
+
132
+ // In your password reset handler
133
+ const resetUrl = `${process.env.NEXT_PUBLIC_APP_URL}/reset-password?token=${token}`;
134
+ await sendPasswordResetEmail(user.email, resetUrl);
135
+ ```
136
+
137
+ ### Sending a Custom Email
138
+
139
+ ```typescript
140
+ import { sendEmail } from "@/lib/email";
141
+ import { MyCustomEmail } from "@/emails/my-custom-email";
142
+
143
+ await sendEmail({
144
+ to: "user@example.com",
145
+ subject: "Custom Email Subject",
146
+ react: MyCustomEmail({ customProp: "value" }),
147
+ from: "Custom Sender <custom@yourdomain.com>", // Optional
148
+ });
149
+ ```
150
+
151
+ ### Creating a New Email Template
152
+
153
+ 1. Create a new file in `emails/`:
154
+ ```typescript
155
+ // emails/my-email.tsx
156
+ import { Text } from '@react-email/components';
157
+ import { EmailLayout } from './components/layout';
158
+
159
+ interface MyEmailProps {
160
+ userName: string;
161
+ }
162
+
163
+ export function MyEmail({ userName }: MyEmailProps) {
164
+ return (
165
+ <EmailLayout preview="My email preview">
166
+ <Text>Hello {userName}!</Text>
167
+ </EmailLayout>
168
+ );
169
+ }
170
+ ```
171
+
172
+ 2. Add a helper function in `lib/email.ts`:
173
+ ```typescript
174
+ export async function sendMyEmail(email: string, userName: string) {
175
+ const { MyEmail } = await import('@/emails/my-email');
176
+ return sendEmail({
177
+ to: email,
178
+ subject: 'My Email Subject',
179
+ react: MyEmail({ userName }),
180
+ });
181
+ }
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Customization
187
+
188
+ ### Styling
189
+ - Email templates use React Email components
190
+ - Customize styles in individual email templates
191
+ - Shared layout in `emails/components/layout.tsx`
192
+
193
+ ### Configuration
194
+ - Email configuration is in `lib/email.ts`
195
+ - Customize default sender in `EMAIL_FROM` env var
196
+ - Modify email templates in `emails/` directory
197
+
198
+ ### Templates
199
+ - All templates are in `emails/` directory
200
+ - Use `EmailLayout` component for consistent styling
201
+ - Templates are React components - fully customizable
202
+
203
+ ---
204
+
205
+ ## Integration Points
206
+
207
+ ### Files That May Need Manual Updates
208
+
209
+ **lib/auth.ts** (if auth module installed):
210
+ - Already imports email functions
211
+ - Will automatically use real email sending when email module is installed
212
+ - Falls back to console logging if email module not installed
213
+
214
+ **lib/subscription.ts** (if payments module installed):
215
+ - Subscription confirmation emails will work automatically
216
+ - Payment failed emails will work automatically
217
+
218
+ **No manual integration needed!** The email module integrates automatically with auth and payments modules.
219
+
220
+ ---
221
+
222
+ ## Troubleshooting
223
+
224
+ ### Common Issues
225
+
226
+ **Issue:** "Email not sent (Resend not configured)" in console
227
+ **Solution:**
228
+ - Add `RESEND_API_KEY` to `.env.local`
229
+ - Get API key from [resend.com](https://resend.com)
230
+ - Restart dev server after adding env var
231
+
232
+ **Issue:** Emails not being received
233
+ **Solution:**
234
+ - Check Resend dashboard for delivery status
235
+ - Verify sender email is verified in Resend
236
+ - Check spam folder
237
+ - Verify `EMAIL_FROM` matches verified domain
238
+
239
+ **Issue:** "Invalid API key" error
240
+ **Solution:**
241
+ - Verify `RESEND_API_KEY` is correct
242
+ - Check for extra spaces or quotes in `.env.local`
243
+ - Regenerate API key in Resend dashboard if needed
244
+
245
+ **Issue:** Domain not verified
246
+ **Solution:**
247
+ - In development, you can use Resend's test domain
248
+ - For production, verify your domain in Resend dashboard
249
+ - Add DNS records as instructed by Resend
250
+
251
+ ---
252
+
253
+ ## Next Steps
254
+
255
+ After installing this module:
256
+
257
+ 1. Get Resend API key from [resend.com](https://resend.com)
258
+ 2. Add `RESEND_API_KEY` to `.env.local`
259
+ 3. Test email sending (welcome email, password reset, etc.)
260
+ 4. Verify your domain for production
261
+ 5. Customize email templates to match your brand
262
+ 6. Set up custom `EMAIL_FROM` address
263
+
264
+ ---
265
+
266
+ ## Related Modules
267
+
268
+ This module works well with:
269
+ - **Auth Module** - For password reset emails
270
+ - **Payments Module** - For subscription confirmation and payment failed emails
271
+
272
+ **Note:** Email module is optional. Auth and Payments modules work without it (they log to console instead of sending emails).
273
+
274
+ ---
275
+
276
+ ## Module Status
277
+
278
+ ✅ **Standalone Package** - Can be installed independently
279
+ ✅ **Graceful Fallback** - Works without configuration (logs to console)
280
+ ✅ **Ready to Use** - Works immediately after append
281
+ ✅ **Well Documented** - Comprehensive integration guide
282
+
@@ -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;