Skip to content

Commit

Permalink
Adding Contact controller and routes
Browse files Browse the repository at this point in the history
  • Loading branch information
bertrandshema committed Jul 21, 2024
1 parent 189d16f commit 9ab0a9b
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 3 deletions.
32 changes: 32 additions & 0 deletions src/__test__/contact.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import request from 'supertest';
import express from 'express';
import { handleContact } from '../controller/contactController'; // Adjust the import path accordingly

const app = express();
app.use(express.json());
app.post('/api/v1/contact', handleContact);

describe('POST /api/v1/contact', () => {
beforeEach(() => {
process.env.NODE_ENV = 'test'; // Ensure we're in test mode
});

it('should return 400 if name, email, or message is missing', async () => {
await request(app)
.post('/api/v1/contact')
.send({ email: 'test@example.com', message: 'Hello' })
.expect(400)
.expect({ error: 'Name, email, and message are required' });
});

it('should send feedback successfully', async () => {
await request(app)
.post('/api/v1/contact')
.send({ name: 'John Doe', email: 'john@example.com', message: 'Hello' })
.expect(200)
.expect({ message: 'Feedback sent successfully' });
});

// Since we're not testing the actual email sending functionality here,
// there's no need for a test that simulates failure to send feedback.
});
26 changes: 26 additions & 0 deletions src/controller/contactController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Request, Response } from 'express';
import sendEmail from '../emails/contact';

export const handleContact = async (req: Request, res: Response) => {
const { name, email, phone, message } = req.body;

if (!name || !email || !message) {
return res
.status(400)
.json({ error: 'Name, email, and message are required' });
}

try {
if (process.env.NODE_ENV !== 'test') {
await sendEmail('contact', 'bentopride@gmail.com', {
name,
email,
phone,
message,
});
}
res.status(200).json({ message: 'Feedback sent successfully' });
} catch (error) {
res.status(500).json({ error: 'Failed to send feedback' });
}
};
68 changes: 68 additions & 0 deletions src/docs/contact.doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* @swagger
* tags:
* name: Contact
* description: Contact management
*/
/**
* @swagger
* /api/v1/contact:
* post:
* summary: Handle contact form submissions
* description: This endpoint handles contact form submissions and sends the data via email to administrators.
* tags: [Contact]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* example: John Doe
* email:
* type: string
* example: john.doe@example.com
* phone:
* type: string
* example: 123-456-7890
* message:
* type: string
* example: This is a test message.
* required:
* - name
* - email
* - message
* responses:
* 200:
* description: Feedback sent successfully
* content:
* application/json:
* schema:
* type: object
* properties:
* message:
* type: string
* example: Feedback sent successfully
* 400:
* description: Name, email, and message are required
* content:
* application/json:
* schema:
* type: object
* properties:
* error:
* type: string
* example: Name, email, and message are required
* 500:
* description: Failed to send feedback
* content:
* application/json:
* schema:
* type: object
* properties:
* error:
* type: string
* example: Failed to send feedback
*/
62 changes: 62 additions & 0 deletions src/emails/contact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import axios from 'axios';
import handlebars from 'handlebars';
import fs from 'fs';
type EmailType = 'contact';
type Data = {
name: string;
phone: string;
email: string;
message: string;
};
/**
* Sends an email of the specified type to the recipient using the provided data.
*
* @param emailType - The type of email to send. Must be either "confirm" or "reset".
* @param recipient - The email address of the recipient.
* @param data - The data to be used for generating the email content. A name and link are required.
* @returns A Promise that resolves to the response from the email service.
* @throws An error if there is an issue sending the email.
*/
async function sendEmail(emailType: EmailType, recipient: string, data: Data) {
const templatePath = './src/emails/templates/contact.html';
try {
// Read the Handlebars template file
const templateFile = fs.readFileSync(templatePath, 'utf-8');

// Compile the template
const template = handlebars.compile(templateFile);

// Generate the HTML content using the template and data
const html = template(data);

// Send the Email

const domain = process.env.MAILGUN_DOMAIN;
const key = process.env.MAILGUN_TOKEN as string;
const body = {
from: `Dynamites Account Team <info@${domain}>`,
to: [recipient],
subject: 'New Contact',
html: html,
};
const mailgunResponse = await axios.post(
`https://api.mailgun.net/v3/${domain}/messages`,
body,
{
auth: {
username: 'api',
password: key,
},
headers: {
'Content-Type': 'multipart/form-data',
},
}
);

return mailgunResponse;
} catch (error) {
throw new Error(`Error sending email: ${error}`);
}
}

export default sendEmail;
2 changes: 1 addition & 1 deletion src/emails/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ async function sendEmail(emailType: EmailType, recipient: string, data: Data) {
}
}

export default sendEmail;
export default sendEmail;
133 changes: 133 additions & 0 deletions src/emails/templates/contact.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap"
rel="stylesheet"
/>
<style>
body {
font-family: 'Open Sans', sans-serif;
background-color: #f7fafc;
}

.container {
min-height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
padding: 12px;
box-sizing: border-box;
background-color: #f7fafc;
}

.card {
max-width: 720px;
width: 100%;
background-color: #ffffff;
padding: 24px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.title {
margin-top: 24px;
margin-bottom: 16px;
font-size: 24px;
font-weight: bold;
color: #1a202c;
text-align: center;
}

.message {
margin-bottom: 16px;
font-size: 14px;
color: #4a5568;
text-align: center;
}

.button {
display: block;
width: 100%;
padding: 12px;
border: none;
border-radius: 4px;
font-size: 14px;
font-weight: bold;
color: #f7fafc;
background-color: #4c51bf;
text-align: center;
text-decoration: none;
cursor: pointer;
}

.button:hover {
background-color: #434190;
}

.link {
color: #4c51bf;
text-decoration: none;
}

.link:hover {
color: #434190;
}
table {
width: 100%;
padding: 0;
border-width: 0;
}
th,
td {
padding: 0;
margin-bottom: 32px;
border-width: 0;
text-align: left;
font-size: 17px;
}
th {
width: 30%;
}
td {
width: 70%;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="title">New Message</div>
<p class="message">hello Admin,</p>
<p class="message">
you have new message from {{name}}. Here are the message details
</p>
<div>
<table>
<tr>
<th>Full Name:</th>
<td>{{name}}</td>
</tr>
<tr>
<th>Email:</th>
<td>{{email}}</td>
</tr>
<tr>
<th>Phone Number:</th>
<td>{{phone}}</td>
</tr>
<tr>
<th>Message:</th>
<td>
<p>{{message}}</p>
</td>
</tr>
</table>
</div>
</div>
</div>
</body>
</html>
9 changes: 9 additions & 0 deletions src/routes/contactRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Router } from 'express';

import { handleContact } from '../controller/contactController';

const contactRoutes = Router();

contactRoutes.route('/').post(handleContact);

export default contactRoutes;
6 changes: 4 additions & 2 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import couponRouter from './couponRoute';
import chekoutRoutes from './checkoutRoutes';
import reviewRoute from './reviewRoutes';
import orderRoutes from './orderRoutes';
import noticificationRoute from './notificationRoutes'
import noticificationRoute from './notificationRoutes';
import contactRoutes from './contactRoutes';
const router = Router();

router.use('/user', userRouter);
Expand All @@ -22,5 +23,6 @@ router.use('/coupons', couponRouter);
router.use('/checkout', chekoutRoutes);
router.use('/review', reviewRoute);
router.use('/order', orderRoutes);
router.use('/notification',noticificationRoute)
router.use('/notification', noticificationRoute);
router.use('/contact', contactRoutes);
export default router;

0 comments on commit 9ab0a9b

Please sign in to comment.