preview-fix
This commit is contained in:
parent
ecf7b549a3
commit
5870fbb4d8
|
|
@ -1,11 +1,12 @@
|
|||
import React from 'react';
|
||||
import { render } from '@react-email/render';
|
||||
import { WelcomeEmail } from '../../../../emails/welcome';
|
||||
import { TransactionCompleteEmail } from '../../../../emails/transaction-complete';
|
||||
import { EventTicketEmail } from '../../../../emails/event-ticket';
|
||||
import { PaymentRequestEmail } from '../../../../emails/payment-request';
|
||||
import { ReferralSuccessEmail } from '../../../../emails/referral-success';
|
||||
import { WaitlistEmail } from '../../../../emails/waitlist';
|
||||
import { NextResponse } from 'next/server';
|
||||
import React from 'react';
|
||||
|
||||
const templates: Record<string, React.ComponentType<any>> = {
|
||||
welcome: WelcomeEmail,
|
||||
|
|
@ -13,6 +14,7 @@ const templates: Record<string, React.ComponentType<any>> = {
|
|||
'event-ticket': EventTicketEmail,
|
||||
'payment-request': PaymentRequestEmail,
|
||||
'referral-success': ReferralSuccessEmail,
|
||||
waitlist: WaitlistEmail,
|
||||
};
|
||||
|
||||
export async function GET(
|
||||
|
|
@ -26,7 +28,7 @@ export async function GET(
|
|||
return NextResponse.json({ error: 'Template not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const html = render(React.createElement(TemplateComponent));
|
||||
const html = await render(React.createElement(TemplateComponent));
|
||||
|
||||
return new NextResponse(html, {
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import type { Metadata } from 'next';
|
||||
import { ReactNode } from 'react';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Amba Email Templates',
|
||||
|
|
@ -8,10 +9,13 @@ export const metadata: Metadata = {
|
|||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@ const templates = [
|
|||
id: 'referral-success',
|
||||
name: 'Referral Success',
|
||||
},
|
||||
{
|
||||
id: 'waitlist',
|
||||
name: 'Waitlist',
|
||||
},
|
||||
];
|
||||
|
||||
export default function Home() {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react';
|
||||
import { Button as ReactEmailButton } from '@react-email/components';
|
||||
import { theme } from '../theme';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react';
|
||||
import { Section, Text } from '@react-email/components';
|
||||
import { theme } from '../theme';
|
||||
|
||||
|
|
@ -9,10 +10,11 @@ export const Card = ({ children }: CardProps) => {
|
|||
return (
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: theme.colors.accentLight,
|
||||
backgroundColor: theme.colors.accentLightest,
|
||||
borderRadius: '8px',
|
||||
padding: theme.spacing.md,
|
||||
margin: `${theme.spacing.md} 0`,
|
||||
border: `1px solid ${theme.colors.accentLight}`,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Body,
|
||||
Container,
|
||||
|
|
@ -26,7 +27,9 @@ export const EmailLayout = ({
|
|||
}: EmailLayoutProps) => {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Head>
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||
</Head>
|
||||
<Preview>{preview}</Preview>
|
||||
<Body style={main}>
|
||||
<Container style={container}>
|
||||
|
|
@ -42,7 +45,7 @@ export const EmailLayout = ({
|
|||
{children}
|
||||
<Section style={footer}>
|
||||
<Text style={footerText}>
|
||||
© {new Date().getFullYear()} Amba. All rights reserved.
|
||||
© {new Date().getFullYear()} AmbaPay LLC. All rights reserved.
|
||||
</Text>
|
||||
<Text style={footerText}>
|
||||
<Link href="#" style={footerLink}>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
import {
|
||||
Heading,
|
||||
Section,
|
||||
Text,
|
||||
} from '@react-email/components';
|
||||
import { EmailLayout } from './components/EmailLayout';
|
||||
import { Card } from './components/Card';
|
||||
import { Button } from './components/Button';
|
||||
import { theme } from './theme';
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import { EmailLayout } from "./components/EmailLayout";
|
||||
import { Card } from "./components/Card";
|
||||
import { Button } from "./components/Button";
|
||||
import { theme } from "./theme";
|
||||
|
||||
interface PaymentRequestEmailProps {
|
||||
userName?: string;
|
||||
|
|
@ -18,26 +14,23 @@ interface PaymentRequestEmailProps {
|
|||
}
|
||||
|
||||
export const PaymentRequestEmail = ({
|
||||
userName = 'Kirubel',
|
||||
requesterName = 'Sarah Johnson',
|
||||
amount = '$250.00',
|
||||
description = 'Dinner bill split - Italian Restaurant',
|
||||
requestId = 'REQ-987654321',
|
||||
dueDate = 'July 20, 2024',
|
||||
userName = "Kirubel",
|
||||
requesterName = "Sarah Johnson",
|
||||
amount = "$250.00",
|
||||
description = "Dinner bill split - Italian Restaurant",
|
||||
requestId = "REQ-987654321",
|
||||
dueDate = "July 20, 2024",
|
||||
}: PaymentRequestEmailProps) => {
|
||||
return (
|
||||
<EmailLayout
|
||||
preview={`${requesterName} has sent you a payment request for ${amount}.`}
|
||||
>
|
||||
<Section>
|
||||
<Heading style={heading}>
|
||||
New Payment Request 💰
|
||||
</Heading>
|
||||
<Heading style={heading}>New Payment Request 💰</Heading>
|
||||
<Text style={text}>Hi {userName},</Text>
|
||||
<Text style={text}>
|
||||
Hi {userName},
|
||||
</Text>
|
||||
<Text style={text}>
|
||||
<strong>{requesterName}</strong> has sent you a payment request. Here are the details:
|
||||
<strong>{requesterName}</strong> has sent you a payment request. Here
|
||||
are the details:
|
||||
</Text>
|
||||
<Card>
|
||||
<Section style={detailRow}>
|
||||
|
|
@ -64,7 +57,8 @@ export const PaymentRequestEmail = ({
|
|||
)}
|
||||
</Card>
|
||||
<Text style={text}>
|
||||
You can review and pay this request directly from the app. Payment is secure and will be processed immediately.
|
||||
You can review and pay this request directly from the app. Payment is
|
||||
secure and will be processed immediately.
|
||||
</Text>
|
||||
<Section style={buttonSection}>
|
||||
<Button href="https://amba.app/payment-requests">
|
||||
|
|
@ -72,10 +66,13 @@ export const PaymentRequestEmail = ({
|
|||
</Button>
|
||||
</Section>
|
||||
<Text style={noteText}>
|
||||
<strong>Note:</strong> This payment request will remain pending until you complete the payment. You'll receive a confirmation email once the payment is processed.
|
||||
<strong>Note:</strong> This payment request will remain pending until
|
||||
you complete the payment. You'll receive a confirmation email once the
|
||||
payment is processed.
|
||||
</Text>
|
||||
<Text style={text}>
|
||||
Best regards,<br />
|
||||
Best regards,
|
||||
<br />
|
||||
The Amba Team
|
||||
</Text>
|
||||
</Section>
|
||||
|
|
@ -85,75 +82,75 @@ export const PaymentRequestEmail = ({
|
|||
|
||||
const heading = {
|
||||
color: theme.colors.primary,
|
||||
fontSize: '28px',
|
||||
fontWeight: '700',
|
||||
margin: '0 0 24px',
|
||||
fontSize: "28px",
|
||||
fontWeight: "700",
|
||||
margin: "0 0 24px",
|
||||
};
|
||||
|
||||
const text = {
|
||||
color: theme.colors.textDark,
|
||||
fontSize: '16px',
|
||||
lineHeight: '24px',
|
||||
margin: '0 0 16px',
|
||||
fontSize: "16px",
|
||||
lineHeight: "24px",
|
||||
margin: "0 0 16px",
|
||||
};
|
||||
|
||||
const detailRow = {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: '12px',
|
||||
paddingBottom: '12px',
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
marginBottom: "12px",
|
||||
paddingBottom: "12px",
|
||||
borderBottom: `1px solid ${theme.colors.border}`,
|
||||
alignItems: 'flex-start',
|
||||
alignItems: "flex-start",
|
||||
};
|
||||
|
||||
const detailLabel = {
|
||||
color: theme.colors.text,
|
||||
fontSize: '14px',
|
||||
fontWeight: '600',
|
||||
margin: '0',
|
||||
flex: '1',
|
||||
fontSize: "14px",
|
||||
fontWeight: "600",
|
||||
margin: "0",
|
||||
flex: "1",
|
||||
};
|
||||
|
||||
const detailValue = {
|
||||
color: theme.colors.textDark,
|
||||
fontSize: '14px',
|
||||
margin: '0',
|
||||
textAlign: 'right' as const,
|
||||
flex: '1',
|
||||
fontSize: "14px",
|
||||
margin: "0",
|
||||
textAlign: "right" as const,
|
||||
flex: "1",
|
||||
};
|
||||
|
||||
const detailAmount = {
|
||||
color: theme.colors.primary,
|
||||
fontSize: '18px',
|
||||
fontWeight: '700',
|
||||
margin: '0',
|
||||
textAlign: 'right' as const,
|
||||
flex: '1',
|
||||
fontSize: "18px",
|
||||
fontWeight: "700",
|
||||
margin: "0",
|
||||
textAlign: "right" as const,
|
||||
flex: "1",
|
||||
};
|
||||
|
||||
const buttonSection = {
|
||||
textAlign: 'center' as const,
|
||||
margin: '32px 0',
|
||||
textAlign: "center" as const,
|
||||
margin: "32px 0",
|
||||
};
|
||||
|
||||
const noteText = {
|
||||
color: theme.colors.text,
|
||||
fontSize: '14px',
|
||||
lineHeight: '20px',
|
||||
margin: '24px 0 16px',
|
||||
padding: '12px',
|
||||
backgroundColor: '#fff9e6',
|
||||
borderRadius: '4px',
|
||||
color: theme.colors.textDark,
|
||||
fontSize: "14px",
|
||||
lineHeight: "20px",
|
||||
margin: "24px 0 16px",
|
||||
padding: "12px",
|
||||
backgroundColor: theme.colors.accentLightest,
|
||||
borderRadius: "4px",
|
||||
borderLeft: `3px solid ${theme.colors.accent}`,
|
||||
};
|
||||
|
||||
PaymentRequestEmail.PreviewProps = {
|
||||
userName: 'Kirubel',
|
||||
requesterName: 'Sarah Johnson',
|
||||
amount: '$250.00',
|
||||
description: 'Dinner bill split - Italian Restaurant',
|
||||
requestId: 'REQ-987654321',
|
||||
dueDate: 'July 20, 2024',
|
||||
userName: "Kirubel",
|
||||
requesterName: "Sarah Johnson",
|
||||
amount: "$250.00",
|
||||
description: "Dinner bill split - Italian Restaurant",
|
||||
requestId: "REQ-987654321",
|
||||
dueDate: "July 20, 2024",
|
||||
} as PaymentRequestEmailProps;
|
||||
|
||||
export default PaymentRequestEmail;
|
||||
|
|
|
|||
3
emails/react-email.d.ts
vendored
3
emails/react-email.d.ts
vendored
|
|
@ -1,3 +0,0 @@
|
|||
declare module '@react-email/components' {
|
||||
export * from '@react-email/components';
|
||||
}
|
||||
|
|
@ -1,16 +1,19 @@
|
|||
export const theme = {
|
||||
colors: {
|
||||
primary: '#2d5016', // Dark green
|
||||
primaryLight: '#4a7c2a', // Medium green
|
||||
accent: '#7cb342', // Light green
|
||||
accentLight: '#aed581', // Lighter green
|
||||
text: '#8d6e63', // Light brown/orange for text
|
||||
textDark: '#5d4037', // Darker brown for headings
|
||||
primary: '#105D38', // Primary green
|
||||
primaryLight: '#2A7D4A', // Lighter green
|
||||
primaryLightest: '#E6F4EC', // Very light green background
|
||||
accent: '#FFB668', // Primary orange
|
||||
accentLight: '#FFC88A', // Lighter orange
|
||||
accentLightest: '#FFF4E8', // Very light orange background
|
||||
text: '#FFB668', // Orange for text accents
|
||||
textDark: '#105D38', // Green for headings and dark text
|
||||
textMuted: '#666666', // Muted text color
|
||||
background: '#ffffff',
|
||||
border: '#e0e0e0',
|
||||
border: '#E0E0E0', // Light border
|
||||
},
|
||||
fonts: {
|
||||
sans: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
||||
sans: '"DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
||||
},
|
||||
spacing: {
|
||||
xs: '8px',
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// SVG logo as data URI for email compatibility
|
||||
// In production, you should host this logo and use the hosted URL
|
||||
|
||||
// SVG with fill color set to match theme
|
||||
const logoSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 86.42 80.35"><g><path fill="#2d5016" d="m86.42,70.54h-8.94l-4.29-7.02c2.34-1.06,4.47-2.49,6.34-4.25l6.9,11.27Zm-3.16-28.6c0-10.99-8.94-19.92-19.92-19.92h-18.76l-25.06,40.9h-5.91L43.21,14.59l2.02,3.29h8.94L43.22,0,0,70.54h20.49c2.05,0,3.96-1.07,5.02-2.81l20.9-34.12,2.43-3.97h14.49c6.78,0,12.3,5.52,12.3,12.3,0,2.68-.86,5.16-2.33,7.18-1.51,2.1-3.67,3.7-6.18,4.51-1.2.39-2.47.6-3.8.6h-16.88c-3.48,0-6.77,1.85-8.59,4.81l-13.05,21.3h5.64c2.04,0,3.95-1.07,5.02-2.81l8.89-14.51c.44-.72,1.24-1.17,2.09-1.17h16.88c2.78,0,5.44-.57,7.85-1.61,2.37-1.02,4.51-2.49,6.31-4.31,3.57-3.6,5.77-8.56,5.77-14.01Zm-17.3,8.07c1.69-.55,3.21-1.65,4.25-3.11.34-.46.62-.95.84-1.46l-7.25-11.83h-8.94l10.19,16.64c.31-.05.61-.14.91-.24Z"/></g></svg>`;
|
||||
// SVG with fill color set to match theme (green #105D38)
|
||||
const logoSvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 86.42 80.35"><g><path fill="#105D38" d="m86.42,70.54h-8.94l-4.29-7.02c2.34-1.06,4.47-2.49,6.34-4.25l6.9,11.27Zm-3.16-28.6c0-10.99-8.94-19.92-19.92-19.92h-18.76l-25.06,40.9h-5.91L43.21,14.59l2.02,3.29h8.94L43.22,0,0,70.54h20.49c2.05,0,3.96-1.07,5.02-2.81l20.9-34.12,2.43-3.97h14.49c6.78,0,12.3,5.52,12.3,12.3,0,2.68-.86,5.16-2.33,7.18-1.51,2.1-3.67,3.7-6.18,4.51-1.2.39-2.47.6-3.8.6h-16.88c-3.48,0-6.77,1.85-8.59,4.81l-13.05,21.3h5.64c2.04,0,3.95-1.07,5.02-2.81l8.89-14.51c.44-.72,1.24-1.17,2.09-1.17h16.88c2.78,0,5.44-.57,7.85-1.61,2.37-1.02,4.51-2.49,6.31-4.31,3.57-3.6,5.77-8.56,5.77-14.01Zm-17.3,8.07c1.69-.55,3.21-1.65,4.25-3.11.34-.46.62-.95.84-1.46l-7.25-11.83h-8.94l10.19,16.64c.31-.05.61-.14.91-.24Z"/></g></svg>`;
|
||||
|
||||
// Encode SVG for data URI
|
||||
const encodedSvg = encodeURIComponent(logoSvg);
|
||||
|
|
|
|||
83
emails/waitlist.tsx
Normal file
83
emails/waitlist.tsx
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import {
|
||||
Heading,
|
||||
Section,
|
||||
Text,
|
||||
} from '@react-email/components';
|
||||
import { EmailLayout } from './components/EmailLayout';
|
||||
import { Button } from './components/Button';
|
||||
import { theme } from './theme';
|
||||
|
||||
interface WaitlistEmailProps {
|
||||
userName?: string;
|
||||
position?: number;
|
||||
}
|
||||
|
||||
export const WaitlistEmail = ({
|
||||
userName = 'Kirubel',
|
||||
position = 42,
|
||||
}: WaitlistEmailProps) => {
|
||||
return (
|
||||
<EmailLayout
|
||||
preview="Thank you for joining the AmbaPay waitlist! We're excited to have you on board."
|
||||
>
|
||||
<Section>
|
||||
<Heading style={heading}>
|
||||
You're on the List! 🎉
|
||||
</Heading>
|
||||
<Text style={text}>
|
||||
Hi {userName},
|
||||
</Text>
|
||||
<Text style={text}>
|
||||
Thank you for joining the AmbaPay waitlist! We're thrilled to have you as part of our community. You're helping us build the future of seamless payments.
|
||||
</Text>
|
||||
<Text style={text}>
|
||||
<strong>Your position: #{position}</strong>
|
||||
</Text>
|
||||
<Text style={text}>
|
||||
We're working hard to launch AmbaPay and bring you an innovative payment experience. As we get closer to launch, we'll keep you updated on our progress and exclusive early access opportunities.
|
||||
</Text>
|
||||
<Section style={buttonSection}>
|
||||
<Button href="https://amba.app/waitlist">
|
||||
Learn More About AmbaPay
|
||||
</Button>
|
||||
</Section>
|
||||
<Text style={text}>
|
||||
In the meantime, feel free to follow us on social media for updates, tips, and behind-the-scenes content.
|
||||
</Text>
|
||||
<Text style={text}>
|
||||
We appreciate your patience and can't wait to welcome you to AmbaPay!
|
||||
</Text>
|
||||
<Text style={text}>
|
||||
Best regards,<br />
|
||||
The AmbaPay Team
|
||||
</Text>
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
|
||||
const heading = {
|
||||
color: theme.colors.primary,
|
||||
fontSize: '28px',
|
||||
fontWeight: '700',
|
||||
margin: '0 0 24px',
|
||||
};
|
||||
|
||||
const text = {
|
||||
color: theme.colors.textDark,
|
||||
fontSize: '16px',
|
||||
lineHeight: '24px',
|
||||
margin: '0 0 16px',
|
||||
};
|
||||
|
||||
const buttonSection = {
|
||||
textAlign: 'center' as const,
|
||||
margin: '32px 0',
|
||||
};
|
||||
|
||||
WaitlistEmail.PreviewProps = {
|
||||
userName: 'Kirubel',
|
||||
position: 42,
|
||||
} as WaitlistEmailProps;
|
||||
|
||||
export default WaitlistEmail;
|
||||
5
next-env.d.ts
vendored
Normal file
5
next-env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
|
||||
7767
package-lock.json
generated
Normal file
7767
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -11,16 +11,16 @@
|
|||
"@react-email/components": "^0.0.25",
|
||||
"@react-email/render": "^1.0.4",
|
||||
"next": "^14.2.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"qrcode": "^1.5.3"
|
||||
"react-dom": "^18.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.14.0",
|
||||
"@types/node": "25.0.2",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@types/react": "^18.3.0",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"react-email": "^2.0.0",
|
||||
"typescript": "^5.4.5"
|
||||
"typescript": "5.9.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"jsx": "preserve",
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
|
|
@ -12,7 +13,6 @@
|
|||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user