preview-fix

This commit is contained in:
kirukib 2025-12-14 22:50:58 +03:00
parent ecf7b549a3
commit 5870fbb4d8
16 changed files with 11474 additions and 88 deletions

View File

@ -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: {

View File

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

View File

@ -24,6 +24,10 @@ const templates = [
id: 'referral-success',
name: 'Referral Success',
},
{
id: 'waitlist',
name: 'Waitlist',
},
];
export default function Home() {

View File

@ -1,3 +1,4 @@
import React from 'react';
import { Button as ReactEmailButton } from '@react-email/components';
import { theme } from '../theme';

View File

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

View File

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

View File

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

View File

@ -1,3 +0,0 @@
declare module '@react-email/components' {
export * from '@react-email/components';
}

View File

@ -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',

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -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"
}
}

View File

@ -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": [
{

3518
yarn.lock Normal file

File diff suppressed because it is too large Load Diff