Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 233 additions & 0 deletions EMAIL_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
# Email Service Integration Guide

The contact form is functional and currently opens your default email client with pre-filled information as a fallback. To enable server-side email sending, you can integrate with any of the following services:

## Option 1: Resend (Recommended)

[Resend](https://resend.com) is a modern email API built for developers with a generous free tier.

### Setup:

1. Install Resend:
```bash
npm install resend
```

2. Sign up at [resend.com](https://resend.com) and get your API key

3. Add to `.env.local`:
```env
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx
[email protected]
```

4. Update `app/api/contact/route.ts`:

```typescript
import { Resend } from 'resend';
import { createEmailHTML } from './route'; // Import the existing HTML template function

const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { firstName, lastName, email, message } = body;

// Validation...

const emailHTML = createEmailHTML(firstName, lastName, email, message);

const { data, error } = await resend.emails.send({
from: 'Portfolio <[email protected]>',
to: [process.env.CONTACT_EMAIL || '[email protected]'],
subject: `Portfolio Contact: ${firstName} ${lastName}`,
replyTo: email,
html: emailHTML
});

if (error) {
throw error;
}

return NextResponse.json({
success: true,
message: `Thank you, ${firstName}! Your message has been sent and I'll get back to you soon.`
});
} catch (error) {
console.error('Contact form error:', error);
return NextResponse.json(
{ error: 'Failed to send message' },
{ status: 500 }
);
}
}
```

**Note:** The email template is already styled to match your portfolio's dark theme with gradient effects, rounded corners, and modern typography. The `createEmailHTML` function is defined in the route file.

## Option 2: SendGrid

[SendGrid](https://sendgrid.com) is a reliable email service with a free tier.

### Setup:

1. Install SendGrid:
```bash
npm install @sendgrid/mail
```

2. Get your API key from SendGrid

3. Add to `.env.local`:
```env
SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxx
[email protected]
```

4. Update `app/api/contact/route.ts`:

```typescript
import sgMail from '@sendgrid/mail';
import { createEmailHTML } from './route'; // Import the existing HTML template function

sgMail.setApiKey(process.env.SENDGRID_API_KEY!);

export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { firstName, lastName, email, message } = body;

// Validation...

const emailHTML = createEmailHTML(firstName, lastName, email, message);

await sgMail.send({
to: process.env.CONTACT_EMAIL || '[email protected]',
from: '[email protected]', // Must be verified in SendGrid
subject: `Portfolio Contact: ${firstName} ${lastName}`,
replyTo: email,
html: emailHTML
});

return NextResponse.json({
success: true,
message: `Thank you, ${firstName}! Your message has been sent and I'll get back to you soon.`
});
} catch (error) {
console.error('Contact form error:', error);
return NextResponse.json(
{ error: 'Failed to send message' },
{ status: 500 }
);
}
}
```

## Option 3: Nodemailer (SMTP)

Use any SMTP provider (Gmail, Office365, custom server).

### Setup:

1. Install Nodemailer:
```bash
npm install nodemailer
npm install -D @types/nodemailer
```

2. Add to `.env.local`:
```env
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
SMTP_PASS=your-app-password
[email protected]
```

3. Update `app/api/contact/route.ts`:

```typescript
import nodemailer from 'nodemailer';
import { createEmailHTML } from './route'; // Import the existing HTML template function

const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT || '587'),
secure: false,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
});

export async function POST(request: NextRequest) {
try {
const body = await request.json();
const { firstName, lastName, email, message } = body;

// Validation...

const emailHTML = createEmailHTML(firstName, lastName, email, message);

await transporter.sendMail({
from: process.env.SMTP_USER,
to: process.env.CONTACT_EMAIL || '[email protected]',
subject: `Portfolio Contact: ${firstName} ${lastName}`,
replyTo: email,
html: emailHTML
});

return NextResponse.json({
success: true,
message: `Thank you, ${firstName}! Your message has been sent and I'll get back to you soon.`
});
} catch (error) {
console.error('Contact form error:', error);
return NextResponse.json(
{ error: 'Failed to send message' },
{ status: 500 }
);
}
}
```

## Current Fallback Behavior

Without configuring an email service, the form will:
1. Validate the input
2. Show a success message
3. Open your default email client with pre-filled information
4. Log the submission to the server console

This ensures the form is always functional, even without an email service configured.

## Testing

You can test the form locally:

```bash
npm run dev
```

Navigate to the contact section and submit the form. Check:
- Form validation works
- Loading state appears during submission
- Success/error messages display correctly
- Email client opens with pre-filled data (if no service configured)
- Server logs show the submission details

## Adding Environment Variables to Vercel

1. Go to your project settings in Vercel
2. Navigate to Settings → Environment Variables
3. Add your chosen service's variables
4. Redeploy the project

## Security Notes

- Never commit `.env.local` to git
- Use environment variables for all sensitive data
- Validate and sanitize all user inputs (already implemented)
- Consider rate limiting for production
- Use CAPTCHA for additional spam protection if needed
34 changes: 29 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Personal portfolio website showcasing software engineering work, projects, and t
- **Responsive Design** - Mobile-first approach with Tailwind CSS
- **Dark/Light Mode** - System preference detection with smooth transitions
- **Interactive Resume** - Dynamic resume page with structured experience data
- **Contact Form** - Functional email form with validation and multiple integration options
- **Smooth Animations** - Typing effects, fade-ins, and scroll-based interactions
- **Production-Ready** - CI/CD pipeline with automated testing and deployment

Expand All @@ -27,7 +28,7 @@ Personal portfolio website showcasing software engineering work, projects, and t
**Testing**
- [Jest 29.7.0](https://jestjs.io/) - Test runner
- [React Testing Library 16.0.1](https://testing-library.com/react) - Component testing
- 77 passing tests with 100% coverage on core features
- 156 passing tests with comprehensive coverage on all features

**DevOps & CI/CD**
- GitHub Actions - Automated testing and deployment
Expand Down Expand Up @@ -62,6 +63,24 @@ Personal portfolio website showcasing software engineering work, projects, and t
http://localhost:3000
```

## ✉️ Email Configuration

**Status: ✅ Working in Testing Mode**

The contact form automatically sends emails via Resend! Currently configured to send to **[email protected]** (testing mode).

**Quick Test:**
```bash
npm run dev
# Go to http://localhost:3000, fill the form, check [email protected]
```

**For Production Setup:**
See [RESEND_SETUP.md](./RESEND_SETUP.md) for instructions on:
- Verifying your domain at Resend
- Sending to [email protected] or any email address
- Production deployment configuration

## 📜 Available Scripts

```bash
Expand Down Expand Up @@ -95,9 +114,14 @@ npm test -- navigation.test.tsx
```

**Test Coverage:**
- 9 test suites
- 77 passing tests
- 100% coverage on hooks, components, and utilities
- 13 test suites
- 156 passing tests
- Comprehensive coverage including:
- Hooks, components, and utilities
- API routes and email service
- Input validation and XSS prevention
- Email template generation
- Integration tests

## 🚢 Deployment

Expand All @@ -110,7 +134,7 @@ npm test -- navigation.test.tsx

On every push and PR:
1. ✅ Lint code with ESLint
2. ✅ Run test suite (77 tests)
2. ✅ Run test suite (156 tests)
3. ✅ Build Next.js application
4. ✅ Deploy to Vercel (main/prod branches only)

Expand Down
Loading
Loading